Protected Routes in React Router v7
Securing some pages of the web application is not an option at this time, but a need. Route package protection is important. Whether you are building an admin panel, a student portal, or a SaaS application, it is necessary to have some way to restrict access to specific routes. This is where route package protection comes into play.
With the release of React Router v7, the developers had a lot more flexibility with regard to how they handle navigation and authentication logic. One such interesting solution is to use Protected Routes in React Router v7 without using higher-order components or even JSX wrappers like .
This article walks you through the clean, scalable, JSX-free approach to route protection using the data-driven routing paradigm of React Router v7.
Main Content
What is Route Package Protection?
Before we start with the implementation, let me first describe what route package protection is.
It means route access control, which depends on the user authentication or authorization. You don’t simply give each page permission to everyone — no, you explicitly state:
- The dashboard is only visible for logged-in users.
- Admin has access to user management only.
- The private pages are to be diverted off the guests.
Why You Should Avoid JSX- Hiding Routes?
Usually, the classic use for React Router is based on a JSX such as:
- Outlet for nested route protection.
However, in React Router v7 (especially in data mode) a more scalable way to do it is:
- Use plain functions
- Avoid JSX wrappers
- Component() Directly call components
Advantages of the No-JSX Approach
- Cleaner route configuration
- Increased segregation of responsibilities.
- Less testing and debugging.
- A more controlled flow of logic.
Setup React Router v7 (Data Mode)
With the new react router v7, the tree structure must be replaced by object notation for your routes.
Basic Routing Configuration
import createBrowserRouter from react-router-domimport Dashboard, of the pages/Dashboard;const router = createBrowserRouter([
{
path: "/login",
Component: Login,
},
{
path: "/dashboard",
Component: Dashboard,
},
]);
With this framework, we can place logic in the route definitions.
Creating a Protected Route Functional Component (No JSX)
Instead of wrapping a component in some JSX, we create a function that decides whether to render or redirect.
Step 1: Authentication Check
const isLoggedIn = () => {
return!! localStorage. getItem("token");
};
Step 2: Protecting the Component with a Wrapper
import redirect, React- router-dom.const ProtectedRoute = (props) => {
return () => {
if (!isAuthenticated()) {
throw redirect("/login");
}
return Component();
};
};
Notice:
- No JSX is used
- We finally make a direct call to Component.
- We don’t use but throw redirect() instead
Protected Routes Implementation in Router Config
Next, we will use this functionality in our route structure.
import Dashboard, of the pages/Dashboard;const router = createBrowserRouter([
{
path: "/dashboard",
Component: ProtectedRoute(Dashboard),
},
]);
This ensures that:
- The dashboard is loaded on successful user authentication.
- Otherwise, the user is redirected to log in.
Role-Based Route Protection (Advanced)
Often, the authentication still isn’t enough. You might want control based on roles.
Example Roles
- Admin
- Teacher
- Student
Step 1: Store Role
localStorage. setItem("role", "admin");
Step 2: Extend ProtectedRoute
protected route const ProtectedRoute = ( Component, allowedRoles = [] ) => {
return () => {
const token = localStorage. getItem("token");
const role = localStorage. getItem("role"); if (!token) {
throw redirect("/login");
} if (allowedRoles. length &&! allowedRoles. includes(role)) {
throw redirect("/unauthorized");
} return Component();
};
};
Step 3: Use It
{
path: "/admin",
Component: ProtectedRoute(AdminPanel, ["admin"]),
}
Comparison: No-JSX vs JSX Approach
| Feature | JSX way | No-JSX way (v7) |
|---|---|---|
| Wrapper Usage | JSX () | Direct Component() call |
| Render | JSX () | Direct Component() call |
| Redirect | Redirect | throw redirect() |
| Difficulty | High | Low |
| Scalability | Moderate | High |
| Code Readability | Mixed | Cleaner |
Mistakes that can be made and should not be made
Using Protected Routes with React Router v7, developers commonly face the following issues:
Using JSX accidentally
return ; // Wrong
Correct:
return Component();
Forgetting the redirect throw
return redirect("/login"); // Wrong
Correct:
throw redirect("/login");
Not managing roles properly
- Ensure that you always authenticate the role before serving protected material.
Best Practices Route Package Protection
To robustly deploy it at scale:
- Centralise authentication logic.
- Do not reimplement defense code.
- Fill in a green storage (can be cookies ).
- States for loading: handle authentication check = async.
- Protect the backend APIs as well; do not simply protect frontend APIs.
Conclusion / Final Thoughts
React Router v7 Protected Routes without using JSX are not just a style of writing; they also provide a much cleaner and maintainable way of controlling route security.
Thanks to widget-based routing and the execution of components directly, you can thin out your codebase while maintaining route safety. This approach works great with the current React trends as well as scales smoothly to your app’s size.
This is something you should consider using all along, if you are building a MERN app, an admin panel, or some other system that requires user roles.
Suggested Reads
- How to Install React Router Dom Complete Guide (2026)
- Critical React Server Components Vulnerabilities in React and Next.js: What Developers Must Do Now?
FAQs
What is routing package protection?
It can be described as the access control (authentication or user role) where only authorized users can access certain parts of an application.
React Router v7 with no JSX work for the Protected Routes now?
Yes, you can do this using React Router v7 & a more functional way in it where you just call Component & throw a redirect to navigate.
Is it better than JSX-based routing?
Yes, that is valid for bigger applications. It provides better clean code, scalability, and separation of logic.
What about asynchronous authentication?
In order to use an async logic, you will have to change the ProtectedRoute function to support loaders or async functions before rendering a component.
At least enough frontend route protection?
No, frontend protection is good for UX, but backend validation is needed to be really secure.