javascriptreact-router

How to Pass props to createBrowserRouter, React-Router V7


I have read that createBrowserRouter should be outside React function, I could not find anyway/tutorial that show how to pass props to the components, should I use (useContext) in this case? Are there any other options?

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<Layout />}>
      <Route index element={<Home articles={articles} />} />
      <Route path="About" element={<About />} />
    </Route>
  )
);

export function App() {
  const [articles, setArticles] = useState();

  return (
    <div>
      <RouterProvider router={router} />
    </div>
  );
}

Solution

  • Yes, you should typically declare the router once outside the React tree, but as you see with your example this simply doesn't work.

    Trivial Suggestion

    A trivial solution is to create and memoize the router within the React tree.

    Example:

    export function App() {
      const [articles, setArticles] = useState();
    
      const router = React.useMemo(() => createBrowserRouter(
        createRoutesFromElements(
          <Route path="/" element={<Layout />}>
            <Route index element={<Home articles={articles} />} />
            <Route path="About" element={<About />} />
          </Route>
        )
      ), [articles]);
    
      return (
        <div>
          <RouterProvider router={router} />
        </div>
      );
    }
    

    Improved Suggestion

    Moving any state and updaters/callbacks into a React context would be an optimization, and would also allow for keeping the router declaration outside the React tree. Here I would recommend integrating the React context as a layout route component and provide the context value on React-Router's Outlet provided context.

    Example:

    const ArticlesLayout = () => {
      const [articles, setArticles] = useState();
    
      return <Outlet context={{ articles, /* any additional values */ }} />;
    };
    

    Wrap only the routes that need to access the articles state and any callbacks.

    const router = createBrowserRouter(
      createRoutesFromElements(
        <Route path="/" element={<Layout />}>
          <Route element={<ArticlesLayout />}>
            <Route index element={<Home />} />
          </Route>
          <Route path="About" element={<About />} />
        </Route>
      )
    );
    
    export function App() {
      return (
        <div>
          <RouterProvider router={router} />
        </div>
      );
    }
    

    The Home component would now access the articles state via the useOutletContext hook.

    import { useOutletContext } from 'react-router';
    
    export const Home = () => {
      const { articles } = useOutletContext();
    
      ...
    };