Things are changing fast in WEB today, and react-router
v6 is in beta already and around the corner. π€
This is just for learning purposes only, react-router v6 is still in beta, use at your own risk
Private routes in v5 and below were done in a specific way using a custom component mostly named PrivateRoute
that was most of the times just a wrapper and composition of basic Route
and Redirect
e.g.
function PrivateRoute(props) {
let { component: Component, children, render, ...rest } = props
let auth = useAuth();
return (
<Route
{...rest}
render={() => auth
? <Component />
: <Redirect to="/login" />
}
/>
);
}
function App() {
return (
<BrowserRouter>
<Route path="/" component={Public} />
<PrivateRoute path="/private" component={Private} />
</BrowserRouter>
);
}
But taking a look at v6 docs it seems that things changed a little bit, and we need to think a little bit different about it.
For info about all API reference see the link
Let's move on.
Some things that we used to create PrivateRoute
have changed a little bit
-
Redirect
is nowNavigate
-
Route
props changed and is just a stub component now - A new component
Routes
appearead
In v6, routes are rendered in such a manner
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Public />} />
<Route path="/private" element={<Private />} />
</Routes>
</BrowserRouter>
);
}
const Public = () => <div>public</div>;
const Private = () => <div>private</div>;
So as you can see, no more render props or component prop.
You need to pass a direct JSX element (don't worry about performance if you do)
Ok now let's take a look at Route
component source code
/**
* Declares an element that should be rendered at a certain URL path.
*
* @see https://reactrouter.com/api/Route
*/
export function Route(_props: RouteProps): React.ReactElement | null {
invariant(
false,
`A <Route> is only ever to be used as the child of <Routes> element, ` +
`never rendered directly. Please wrap your <Route> in a <Routes>.`
);
}
Wait a minute where is the code? π Well actually the parent component Routes
will use the Route
just as a host for the props and children, and do nothing more with the Route
For more info about
Routes
implementation see link
So how we do implement our PrivateRoute
now? π€ If we do some adjustments to PrivateRoute
props, it will look like this
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Public />} />
<PrivateRoute path="/private" element={<Private />} />
</Routes>
</BrowserRouter>
);
}
But this will not work. Routes
will just take the props of PrivateRoute
and ignore it's body totally. Even a console.log inside PrivateRoute
will not be shown.
So what we do? π€ We do some more adjustments to PrivateRoute
function PrivateRoute({ children }) {
const auth = useAuth();
return auth ? <>{children}</> : <Navigate to="/login" />;
}
As you can see we changed Redirect
to Navigate
, and just return children
if user is authenticated. And the usage of it also changes a little bit
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Public />} />
<Route
path="/private"
element={
<PrivateRoute>
<Private />
</PrivateRoute>
}
/>
</Routes>
</BrowserRouter>
);
}
As you can see PrivateRoute
also moves to element
prop.
The implementation of
PrivateRoute
can be done in multiple ways.
Here is a different implementation of PrivateRoute
using Outlet
function PrivateOutlet() {
const auth = useAuth();
return auth ? <Outlet /> : <Navigate to="/login" />;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/private-outlet" element={<PrivateOutlet />}>
<Route element={<Private />} />
</Route>
</Routes>
</BrowserRouter>
);
}
The pros of this is that you can put multiple private sub routes under same route.
For a full example see this Codesandbox
That's all for today. Happy coding! π π β¨
Keep your users secure!
Cover Photo by Maxim Zhgulev on Unsplash
Latest comments (61)
Thanks!
Hello, Just incase you see this, this is my protected route,
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import {
selectIsAuthenticated,
selectUserLoading,
} from "../../features/user/userSlice";
import Loader from "../../components/Layout/Loader";
const ProtectedRoute = ({ Component }) => {
const isAuthenticated = useSelector(selectIsAuthenticated);
const loading = useSelector(selectUserLoading);
if (loading === true) {
return (
);
}
return isAuthenticated ? : ;
};
export default ProtectedRoute;
it Works fine but I want to improve it for user experience, when a user hard reloads the page from a browser, I want the user to be returned back to the page it was on, but it gets directed back to the Home page of the application if the user is logged it, but if the user is not logged it, it will go to the login page,
I also feel the glitch might be from my App.js component, which I will paste part of the code below,
function App() {
const dispatch = useDispatch();
const user = useSelector(selectUser);
const seller = useSelector(selectSeller);
const loading = useSelector(selectUserLoading);
const sellerLoading = useSelector(selectSellerLoading);
const allProducts = useSelector(selectAllProducts);
const allEvents = useSelector(selectAllEvents);
const token = localStorage.getItem("token");
const sellerToken = localStorage.getItem("seller_token");
useEffect(() => {
dispatch(getAllProducts());
dispatch(getAllEvents());
}, [dispatch, sellerToken, token]);
return (
<>
Spot how all warpped the protected Route inside my Route,
Kindly reply
Could you please wrap children with fragment in "PrivateRoute" (<>children</>)?
Thank you!
Hi Alexandru! Why would that be needed π€
You use it like this
Both private components are passed inside as children as an array of JSX Elements
[JSX.Element, JSX.Element]
React knows how to render them.
Hi Andrew!
In TypeScript (although this doesn't apply to your case, it can be confusing for beginners), the PrivateRoute component should return a JSX.Element. If the children prop is not wrapped in a React.Fragment (<>...</>), TypeScript will start to show an error:
PrivateRoute cannot be used as a JSX component.
Its return type 'Element | undefined' is not a valid JSX element.
Type 'undefined' is not assignable to type 'Element | null'.
Since a React component should return a JSX element or null, a correct implementation to solve this problem would be to wrap the children in a React.Fragment like this: <> {children} </>.
Hope this helps...
I see. Although this seems to happen only if the component is typed π€
Having a simple function as a component in TS I see no errors. Nevertheless, thanks for the tip, I'm updating the code.
Yes. For me it was a bit confusing given that my components are typed. Thank you!
Awesome !
I am getting the duplicate records but my keys are different
Is it correct which I have implemented
PrivateRouter.js
import { Outlet, Navigate } from 'react-router-dom';
const PrivateRouter = (props) => {
const firstLogin = localStorage.getItem('firstLogin')
return firstLogin ? :
};
export default PrivateRouter;
App.js
Can you please review again your post, add more context to the subject, and fix the broken code? Ping me after that, and I'll take a look!
How to call this is app.js
import { Outlet, Navigate } from 'react-router-dom';
const PrivateRouter = (props) => {
const firstLogin = localStorage.getItem('firstLogin')
return firstLogin ? :
};
export default PrivateRouter;
Awesome very Good Brother! Many Many Thanks.
Amazing tutorial, thank you β€οΈ
Great
Nice πππ
Awesome man! Huge thanks π
In this react-router version 6 implementation,how i can show alert or popup message for unlogged user when i trying to navigate private route? if user not logged-in it will show the message "please login first" with login button to navigate login page.
if user not logged-in, user always navigate/redirected to home page or other public route.
See this example, when you do the redirect, you can pass some state
in the login component use that state to show the message
dev.to/iamandrewluca/comment/1jjm9
In this react-router version 6 implementation,how i can show alert or popup message for unlogged user when i trying to navigate private route? if user not logged-in it will show the message "please login first" with login button.
"< L i n k to='/login' > login< L i n k >"
if user not logged-in, user always navigate/redirected to home page or other public route.
In this react-router version 6 implementation,how i can show alert or popup message for unlogged user when i trying to navigate private route? if user not logged-in it will show the message "please login first" with login button.
"< L i n k to='/login' > login< L i n k >"
if user not logged-in, user always navigate/redirected to home page or other public route.
thanks