Conditionally rendering Routes with createBrowserRouter
#10223
Replies: 6 comments 4 replies
-
I'm also running into the same issue. I tried putting const App = () => {
const isEnabled = useFeatureFlag()
const router = useMemo(() => {
return createBrowserRouter(isEnabled ? allRoutes : someRoutes)
}, [isEnabled])
return <RouterProvider router={router} />
} I'd be fine with this solution if the whole app didn't remount when |
Beta Was this translation helpful? Give feedback.
-
Hey guys, do y'all know of any solutions? |
Beta Was this translation helpful? Give feedback.
-
I guess none of your solutions will work. Maybe you should not think from the Router-Side but from the route itself: Create a route that checks if a feature is enabled or not. If it's enabled then render the contents in an outlet. If the feature is disabled then do whatever you want: show an error, redirect or what ever you like. But keep in mind that you cannot change the definition of all routes twice or more: it's not going to work. |
Beta Was this translation helpful? Give feedback.
-
The important thing to remember is that the main idea behind the Data APIs brought over from Remix is to decouple routing/data fetching from rendering. Ryan gives a great overview of the reasoning for this in his When to Fetch talk at Reactathon last year. Once decoupled, we can't let render-time information (like hooks) drive the routing tree. We need to know the routing tree before we render the React tree so that we can match routes and fetch data ahead of time. Right now the entire route tree must be defined up-front. Eventually we want to better support the microfrontend and module federation patterns and allow you to discover more routes as the user navigates - but that will still be done external to the render lifecycle. So, if you want to implement "conditional routes" based on a feature flags, the way to do it is not by conditionally defining those routes, but conditionally allowing access: createRoutesFromElements(
<Route path={'/'} element={<Root />}>
<Route path={'products/:productId'} element={<Product />}>
<Route path={'edit'} element={<EditProduct />} />
{/* ❌ Don't conditionally define like this: */}
{/* {isEnabled && <Route path={'delete'} element={<DeleteProduct />} />} */}
{/* ✅ Instead, conditionally allow access like this: */}
<Route
path={'delete'}
loader={() => {
if (!isEnabled) return redirect('/');
return null;
}}
element={<DeleteProduct />} />
</Route>
</Route>
); It's very common to see existing architectures wanting to use hooks (such as the above example of |
Beta Was this translation helpful? Give feedback.
-
I have referenced this thread here: #10496, might be worth reading. |
Beta Was this translation helpful? Give feedback.
-
I solved this by wrapping the createBrowserRouter inside a hook and wrapping the RouterProvider in another Context to call the hook. Custom hook: const useRouters = () => {
const [loggedIn, setLoggedIn ] = useState(true)
const PRIVATE_ROUTES: RouteObject[] = [
{
action: loginAction,
element: <Login />,
path: '/'
},
{
path: routesPaths.appCatalog,
element: {loggedIn ? <Start /> : <Navigate replace to={"/"} />}
}, // <----- or
...( loggedIn ? [
{
path: routesPaths.appCatalog,
element: <Start />
}
] : [])
]
const PUBLIC_ROUTES: RouteObject[] = []
return createBrowserRouter([...PRIVATE_ROUTES, ...PUBLIC_ROUTES])
}
export default useRouters main.tsx: const ContextsProvider = () => {
const ROUTER = useRouters()
return (
<OthersProvider>
<RouterProvider router={ROUTER} /> // <---- or only this alone
<OthersProvider />
)
}
export default ContextsProvider |
Beta Was this translation helpful? Give feedback.
-
Is there any way to conditionally render routes with
createBrowserRouter
? For example if I'm adding a new route, but only want to render it when a feature flag is enabled.e.g.
This is possible using
Routes
, but we lose the data API functionality:Is there any way to achieve this with
createBrowserRouter
?Beta Was this translation helpful? Give feedback.
All reactions