remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
52.65k stars 10.21k forks source link

[Feature]: Nested basename via Router or Routes component #8584

Closed enginustun closed 2 years ago

enginustun commented 2 years ago

What is the new or updated feature that you are suggesting?

Nested basename usage at least as before.

This kind of usage would be great.

<Router>
  <Routes>
    <Route 
      path="/app1" 
      element={
        /* dynamically rendered by kind of microfrontend component */
        <Router basename="app1">
          <Routes>
            <Route 
              path="/app3" 
              element={
                /* dynamically rendered by kind of microfrontend component */
                <Router basename="app1/app3">
                  ...
                </Router>
              }
            />
          </Routes>
        </Router>
      }
    />

    <Route 
      path="/app2" 
      element={
        /* dynamically rendered by kind of microfrontend component */
        <Router basename="app2">
          ...
        </Router>
      }
    />

  </Routes>
</Router>

Why should this feature be included?

In earlier v6 BETAs of library, <Routes> has basename prop. This prop is moved under <Router> in one later beta but this is very necessary for microfrontend-style architectures. Since there is no way to define nested <Router> components, we are not able to create nested paths like /app1/app2/app3/....

image Nested usage of the router component is limited. We were able to do this partly with previous version(^5.2.0) like following;

// main app
<BrowserRouter>
  <Switch>
    <Route path="/app1" {...route} />;
  </Switch>
</BrowserRouter>
// app1 
<BrowserRouter basename="app1">
  <Switch>
    {anonymous.map((route) => (
      <Route {...route} />
    ))}
  </Switch>
</BrowserRouter>
// app2
<BrowserRouter basename="app2">
  <Switch>
    {anonymous.map((route) => (
      <Route {...route} />
    ))}
  </Switch>
</BrowserRouter>

even we could do this;

// app3 that rendering in app1
<BrowserRouter basename="app1/app3">
  <Switch>
    {anonymous.map((route) => (
      <Route {...route} />
    ))}
  </Switch>
</BrowserRouter>
timdorr commented 2 years ago

You shouldn't have to do this at all in v6. Routing is nested, so it takes on the path prefixes of its parent Routes. As long as each frontend is using the exact same version of React Router, and therefore the same context instances internally, this is all provided for you automatically.

enginustun commented 2 years ago

@timdorr, thank you, I've solved thanks to your comment. I had needed to change my perspective. I leave an example for those who are curious.

If you want to get links relative to parent route, you need to define to prop without starting slash /.

<Route 
  path="/app-1/*"
  element={<Link to="app-3">App 3</Link>} {/* its href will be https://example.com/app-1/app-3 */}
>
<Route>

I've implemented a microfrontend routing with v6, and it even allows swithching between microfrontends.

<BrowserRouter>
  <Routes>
    <Route
      path="/app1/*"
      element={<MicroFrontend name="app-1" host="http://localhost:4001" />}
    />
    <Route
      path="/app2/*"
      element={<MicroFrontend name="app-2" host="http://localhost:4002" />}
    />
  </Routes>
</BrowserRouter>;

{
  /* https://example.com/app-1 */
}
<App1>
  <Routes>
    <Route
      path="/"
      element={
        <>
          <Link to="app-3">App 3</Link> {/* https://example.com/app-1/app-3 */}
          <Link to="/app-3">App 3</Link> {/* https://example.com/app-3 */}
          <Link to="/app-2">App 2</Link>
          {/* https://example.com/app-2  This kind of links can be used for navigating between microfrontend applications*/}
        </>
      }
    ></Route>
  </Routes>
</App1>;

But with this way you cannot render App1 standalone. I am using this kind of conditional container Component to render as standalone;

function App1({ mfe }: AppProps) {
  const ContainerComponent = mfe ? React.Fragment : BrowserRouter;

  return (
    <ContainerComponent>
      <Routes>
        <Route
          path="/"
          element={
            <>
              <Link to="app-3">App 3</Link>{" "}
              {/* https://example.com/app-1/app-3 */}
              <Link to="/app-3">App 3</Link> {/* https://example.com/app-3 */}
              <Link to="/app-2">App 2</Link>
              {/* https://example.com/app-2  This kind of links can be used for navigating between microfrontend applications*/}
            </>
          }
        ></Route>
      </Routes>
    </ContainerComponent>
  );
}