React Router Conditional Protected Route

Saturday, December 20, 2025

I'm using react-oidc-context package for my app. And so far, all routes on my app is protected. However, I need to let some routes to not require authentication. I can't find much documentation on how to do that on React Router v7 Framework mode. So, I start with gathering all relevant information.


To protect a route using react-oidc-context is by providing the component that we want to protect (https://github.com/authts/react-oidc-context?tab=readme-ov-file#protect-a-route).


However, the Framework mode of React Router v7 route config is made up of strings, so I can't just do something like:

route('/some-route', withAuthenticationRequired('path-to-file'))

Back in React Router v6, since I was using either Data or Declarative mode, I can just create AuthenticationGuard component and apply it on the route itself similar to this Auth0 guide. But obviously can't be applied to React Router v7 Framework mode.


In my case, I want to protect a parent Layout component, so all the underlying routes will be protected as well. And I will put unprotected route outside of the protected Layout component. So, I went from:

export default withAuthenticationRequired(Outlet, {
  OnRedirecting: () => (<div>Redirecting to the login page...</div>)
})

to

export default withAuthenticationRequired(Layout, {
  OnRedirecting: () => (<div>Redirecting to the login page...</div>)
})

The code above was in root.tsx and it didn't work for me.


And as a good keeping-up-with-the-tech engineer, I check with AI. The recommended way was to use middleware. I'm familar with middleware concept on .NET OWIN era, so it makes sense to me. As I explore it at this time, the middleware is still under testing for Framework mode and it is an opt-in only. In other words, it probably will change in the future, so I'd rather wait until it stabilize a bit more.


Eventually, after more trials and errors, I got it working by moving the withAuthenticationRequired method from root.tsx file to Layout.tsx file.


My route looks like the following:

export default [
  layout('./routes/layout.tsx', [
    route("protected", "./routes/protected.tsx")
  ]),
  route("unprotected", "./routes/unprotected.tsx")  
] satisfies RouteConfig;

And my Layout.tsx:

const Layout = () => {
...
}

export default withAuthenticationRequired(Layout, {
  OnRedirecting: () => (<div>Redirecting to the login page...</div>)
})