DEV Community

Cover image for πŸ” Private Route in React Router v6

πŸ” Private Route in React Router v6

Andrei Luca on September 21, 2021

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, r...
Collapse
 
thatgriffith profile image
Kevin H Griffith

Awesome man! Huge thanks πŸ™

Collapse
 
ficazzo profile image
Femi Obadimu

thanks

Collapse
 
aspyryan profile image
Aspyryan

I was using a module to route users to I only need to render the header once etc.. so in I have outlet to render the nested routes. How can I now use the with the nested routes. This was my homeModule

return (<>
<Header selectedPage={0} />
<main className="w-main flex-1 shadow-md m-auto p-5 my-5 rounded-sm flex">
  <Outlet/>
</main>
</>);
Enter fullscreen mode Exit fullscreen mode

And this is how I would want to have it work:

<Route path="/" element={<PrivateOutlet />}>
  <Route exact path="/requests" element={<RequestsPage />} />
  ...
</Route>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
aspyryan profile image
Aspyryan

Whoops, I already figured it out, this was the code for my privateoutlet:

export default function PrivateOutlet() {
    const { account } = useContext(AccountContext);

    return account ? (
        <HomeModule>
            <Outlet />
        </HomeModule>
    ) : (
        <Navigate to="/login" replace />
    );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
iamandrewluca profile image
Andrei Luca

Yes 😊 I was about to write that you need to change PrivateOutlet

Collapse
 
tonnov profile image
Antonio Nogueda • Edited

is it necesary write Outlet twice?

Collapse
 
lukrisum profile image
Yeee

Awesome !

Collapse
 
dhenyson profile image
Dhenyson Jhean

Perfeito, muito obrigado (thank you)! But just aesthetic issues I recommend I created the component that manages private routes receiving props and your code is more reduced and clean.

Example

Collapse
 
timbogdanov profile image
Tim Bogdanov • Edited

component={Page} should be capital C component or should at lease be defined:

const PrivateRoute = ({component: Component}) => {
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dongloo profile image
Minh Kì Đông

Great

Collapse
 
vineyrawat profile image
Viney Rawat

ΰ€Άΰ€Ύΰ€¨ΰ€¦ΰ€Ύΰ€° ΰ€²ΰ₯‡ΰ€– ΰ€­ΰ€Ύΰ€ˆ

Collapse
 
mahedi5061 profile image
Mahedi Hassan Sharif

Awesome very Good Brother! Many Many Thanks.

Collapse
 
nichitaa profile image
Pasecinic Nichita

You could use auth-react-router package npmjs.com/package/auth-react-router

It provides a really simple API to define your routes and few more configurations (like redirect routes for authorized and unauthorized routes, fallback component for each of the routes)

Just define your routes path and pages component and it will work

Collapse
 
pabloalmonte profile image
Pablo Junior Almonte Avila

Amazing tutorial, thank you ❀️

Collapse
 
obriankevin11 profile image
obriankevin11 • Edited

How do you protect a set of routes using PrivateRoute?
Repeating ...

<Route path="/private1" element={
            <PrivateRoute>
              <Private1 />
            </PrivateRoute>
          }
        />

<Route path="/private2" element={
            <PrivateRoute>
              <Private2 />
            </PrivateRoute>
          }
        />
Enter fullscreen mode Exit fullscreen mode

... is not very elegant.
Thank you!

Collapse
 
ronakptl996 profile image
Patel Ronak

Nice πŸš€πŸš€πŸš€

Collapse
 
masudur10 profile image
Masudur Rahman

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.

Collapse
 
iamandrewluca profile image
Andrei Luca

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

Collapse
 
masudur10 profile image
Masudur Rahman

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.

Collapse
 
masudur10 profile image
Masudur Rahman • Edited

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.

Collapse
 
stevereid profile image
Steve Reid

I might of missed something, but is there anything wrong with doing something like this?

<Route
  path="/"
  element={user ? <Home /> : <Navigate to="/login" />}
/>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
cebkun profile image
James Russel Ceballos

I think you should navigate to "/dashboard" (or wherever you want him to go after logging in) instead of "/login".

Collapse
 
iamandrewluca profile image
Andrei Luca

πŸ€” looking at this it should work, I don't know if react-router can detect the full tree of routes using this way πŸ€”

Collapse
 
rakeshmokariya profile image
Rakeshmokariya • Edited

I can't understand what is 'children' and what it return ?

function PrivateRoute({ children }) {
const auth = useAuth();
return auth ? children : <Navigate to="/login" />;
}

Collapse
 
iamandrewluca profile image
Andrei Luca

In example bellow children inside PrivateRoute equals <Private />

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Public />} />
        <Route
          path="/private"
          element={
            <PrivateRoute>
              <Private />
            </PrivateRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rakeshmokariya profile image
Rakeshmokariya

Ok i understood.Thank you so much.

Collapse
 
devopshasan profile image
Hasan Habib

Could you please share multiple private route example. What your suggestion to keep the code looks clean?
Thank you.

Thread Thread
 
iamandrewluca profile image
Andrei Luca • Edited

Hey Hasan!

You can put them as adjacent routes

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route
          path="/private-2"
          element={
            <PrivateRoute>
              <Private1 />
            </PrivateRoute>
          }
        />
        <Route
          path="/private-2"
          element={
            <PrivateRoute>
              <Private2 />
            </PrivateRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}
Enter fullscreen mode Exit fullscreen mode

or you can put all private routes in a single parent component that is private

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route
          path="/all-private"
          element={
            <PrivateRoute>
              <AllPrivate />
            </PrivateRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
urafiz profile image
UraFIZ

Whan can we use it in create-react-app?

Collapse
 
iamandrewluca profile image
Andrei Luca • Edited

Whenever you want. CRA does not have a router installed by default. But keep in mind it is in beta.

Collapse
 
urafiz profile image
UraFIZ

Bro, you are mistaken. I gave it a try to use all the new features of v6 and didn't succeed coz something under the hood used a history.push.

Thread Thread
 
iamandrewluca profile image
Andrei Luca • Edited

That CRA does not use a router by default I'm very sure. It depends what version of router do you have installed at the moment.

Thread Thread
 
iamandrewluca profile image
Andrei Luca

See codesandbox liinks at the end. In that examples I'm using CRA

Collapse
 
ian_jones_c4d87258f948f0d profile image
Ian Jones

Your Outlet example doesn't work with react-router-dom v6.0.2 . Any idea why?

Collapse
 
ian_jones_c4d87258f948f0d profile image
Ian Jones

Appears to have broken in react-router-dom 6.0.0-beta.6

Collapse
 
iamandrewluca profile image
Andrei Luca

Updated react-router-dom to last version and updated example.

It seems that you need to pass path to nested routes also, it wasn't the case in earlier versions

it was

<Route path="/private-outlet" element={<PrivateOutlet />}>
  <Route element={<Private />} />
</Route>
Enter fullscreen mode Exit fullscreen mode

now

<Route path="/private-outlet" element={<PrivateOutlet />}>
  <Route path="" element={<Private />} />
</Route>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nivethan profile image
Nivethan Ariyaratnam

thanks man

Collapse
 
surajrp profile image
SURAJ_P

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

      <Routes>
        <Route exact path="/" element={auth.token ? <Home/> : < Login/>} />
        <Route exact path="/register" element={<Register/>}/>
        <Route element={<Layout />}>
          <Route element={<PrivateRouter />}>
            <Route exact path="/:page" element={<PageRender/>}/>
            <Route exact path="/:page/:id" element={<PageRender/>} />
          </Route>
        </Route>
      </Routes>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
iamandrewluca profile image
Andrei Luca

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!

Collapse
 
rajamuhammadasher profile image
Raja Muhammad Asher

Thank you. Helped a lot.

Collapse
 
iamandrewluca profile image
Andrei Luca

You are welcome! πŸ€—

Collapse
 
contriverh profile image
ContriverH

Thanks a lot man. You are the savior

Collapse
 
chikephils profile image
Oreva Chike Philips

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());

if (token) {
  dispatch(LoadUser());
}
if (sellerToken) {
  dispatch(LoadSeller());
}
Enter fullscreen mode Exit fullscreen mode

}, [dispatch, sellerToken, token]);
return (
<>

  <ThemeProvider theme={theme}>
    <div className="w-screen min-h-[100vh] lg:pr-2">
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route
          path="/login"
          element={
            <LoginRoute>
              <LoginPage />
            </LoginRoute>
          }
        />
        <Route
          path="/register"
          element={
            <LoginRoute>
              <RegisterPage />
            </LoginRoute>
          }
        />
        <Route
          path="/payment"
          element={
            <ProtectedRoute>
              <PaymentPage />
            </ProtectedRoute>
          }
        />
        <Route
          path="/activation/:activation_token"
          element={<ActivationPage />}
        />
        <Route
          path="/seller/activation/:activation_token"
          element={<SellerActivationPage />}
        />

        <Route path="/products" element={<ProductsPage />} />
        <Route path="/product/:id" element={<ProductDetailsPage />} />
        <Route path="/best-selling" element={<BestSellingPage />} />
        <Route path="/events" element={<EventPage />} />
        <Route path="/faq" element={<FAQPage />} />
        <Route
          path="/profile"
          element={<ProtectedRoute Component={ProfilePage} />}
        />
        <Route
          path="/profile/orders"
          element={<ProtectedRoute Component={OrderPage} />}
        />
        <Route
          path="/profile/refunds"
          element={<ProtectedRoute Component={RefundPage} />}
        />
        <Route
          path="/profile/inbox"
          element={<ProtectedRoute Component={InboxPage} />}
        />
        <Route
          path="profile/payment-method"
          element={<ProtectedRoute Component={PaymentMethodPage} />}
        />
        <Route
          path="profile/change-password"
          element={<ProtectedRoute Component={ChangePasswordPage} />}
        />
        <Route
          path="profile/address"
          element={<ProtectedRoute Component={AddressPage} />}
        />
Enter fullscreen mode Exit fullscreen mode

Spot how all warpped the protected Route inside my Route,

Kindly reply

Collapse
 
surajrp profile image
SURAJ_P

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;

Collapse
 
alexandrubarbulescu profile image
Alexandru Barbulescu • Edited

Could you please wrap children with fragment in "PrivateRoute" (<>children</>)?

Thank you!

Collapse
 
iamandrewluca profile image
Andrei Luca

Hi Alexandru! Why would that be needed πŸ€”

You use it like this

<PrivateRoute>
  <Private1 />
  <Private2 />
</PrivateRoute>
Enter fullscreen mode Exit fullscreen mode

Both private components are passed inside as children as an array of JSX Elements [JSX.Element, JSX.Element]

React knows how to render them.

Collapse
 
alexandrubarbulescu profile image
Alexandru Barbulescu

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...

Thread Thread
 
iamandrewluca profile image
Andrei Luca

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.

Thread Thread
 
alexandrubarbulescu profile image
Alexandru Barbulescu

Yes. For me it was a bit confusing given that my components are typed. Thank you!

Collapse
 
brandonmcconnell profile image
Brandon McConnell

This article was a lifesaver as I migrate from React Router v5 to v6. Thank you! πŸ†οΈ

Collapse
 
haiderali22 profile image
haiderali22

How can I use this with useRoutes hook provided in react router v6

Collapse
 
iamandrewluca profile image
Andrei Luca
useRoutes([
  {
    path: "/private",
    element: (
      <PrivateRoute>
        <Private />
      </PrivateRoute>
    ),
  },
]);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
arknahian profile image
Al Nahian Ark

Ok, but how can I set a state and redirect the users from there they came from

Collapse
 
iamandrewluca profile image
Andrei Luca

You put some state in Navigation props, and on login after submit redirect back, something like this

function PrivateRoute() {
  const location = useLocation()
  return <Navigation to="/login" state={{ from: location.pathname }} />
}

function Login() {
  const navigate = useNavigate()
  const location = useLocation()

  const onSubmit = () => fetch("/api/login").then(() => navigate(location.state.from))

  return <form onSubmit={onSubmit} />
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
Sloan, the sloth mascot
Comment deleted
 
iamandrewluca profile image
Andrei Luca

You mean to prevent before user is clicking a link? πŸ€” Didn't quite get what exactly do you mean.

Collapse
 
dimasdevspro profile image
Dimas A Pereira

Thanks!

Collapse
 
sanyogchangmai profile image
Sanyog Changmai

Thank you very much....its very helpful

Collapse
 
kisankumavat85 profile image
Kisan Kumavat

It helped a lot, Thanks buddy!

Collapse
 
iamandrewluca profile image
Andrei Luca

πŸ€— Glad could help!

Collapse
 
aaron__ahn profile image
Aaron Ahn

It worked! Thank you so much :D