In this video, I'm going to show you how to
build your own custom protected route component in React. This is a component that you would usually
use whenever you want to protect certain pages or routes in your React application, and only
allow a signed-in user to access them. This is something that is pretty common in
a lot of React applications that have authentication, right? If you have authenticated users, you often
want to protect certain parts of your application and only make sure that signed-in users can
access them. So, get ready, get excited, because you are
going to learn a lot in this video. Alright, let's begin. Let's talk about protected routes in React. So here in front of us, we have a very simple
React application, as always. The only thing that's different about this
application is that we have this piece of code here, which creates a router using the
`createBrowserRouter` function from React Router DOM. React Router DOM, if you're not aware, is
a very popular client-side routing library that you can use to implement routing in your
React application. This video is not going to be a tutorial on
React Router DOM, but don't worry about it, you actually don't need to have a tutorial,
because really, the only thing that we're doing is we're just creating this router,
which basically allows us to set a specific component on a specific path. Right, so here we have the index path, and
here we have the signed-in path. We're rendering the HomePage component on
the index path, and the SignedInPage component on the signed-in path. That's it. And then the only other thing that is different
is here towards the bottom in this piece of code. The only difference is that instead of rendering
our own custom component, we're using the `RouterProvider` from React Router DOM, and
we're passing it this router here. What this is going to do is this is going
to make it so that this router here can actually decide which component to render based on
the path that we're currently on. Then, if we actually look at the implementations
of HomePage and SignedInPage, you're going to see that they're super simple. This component literally does nothing besides
render a simple welcome text right here. The SignedInPage is also the same; it does
nothing besides render "Signed In." We're going to be using this text right here,
this `<h1>` tag, to just differentiate which component we're actually on. For the purposes of this video, we actually
don't need to implement anything else. We don't need to implement our own sign-in
functionality. Instead, what we're going to do is we're going
to manually set the user to be signed in or not. The only interesting thing in our application
so far is this `AuthProvider` component right here. If I open this component, you see that this
one has a little bit of logic. So what this component does, what this file
does, is it first creates a context, an `authContext` here on line five, using the `createContext`
function from React. If this is unfamiliar to you, I do have a
whole tutorial video on `useContext` that you can watch to get yourself up to speed. Then here on line seven, we're actually defining
the types of our `AuthProvider`. So this is this component right here, the
main component that is being exported from this file. We're basically saying that the props of this
component can have children. That's from `PropsWithChildren` here, which
comes from React. And we're also going to allow this isSignedIn
optional property, which, as I said, we're going to use to manually set the user to be
signed in or not. Here in the body of the component, we're using
the `isSignedIn` property to either set a user if it's true or set it to null if it's
false. If you look here at the type of `user`, this
is a very simple type. A user only has an ID. And then here at the bottom, we have a custom
hook called `useAuth`, which basically just makes it easier for us to access the `authContext`
because it makes the check for the undefined case, and then we'll throw an error if the
context is undefined. Otherwise, it is going to return the context. Again, if all of this is unfamiliar to you,
I have a whole video on `useContext`, where we actually implement all of this and I explain
to you why we need a custom hook for our context. So now what we need to do is we need to create
a protected route component that can wrap any page component like this HomePage component,
and then we'll use this `AuthProvider`, we'll basically make a check for the user. And then if we have a user, we are going to
proceed with rendering the actual component. So in this case, the HomePage. Otherwise, we are going to redirect to the
sign-in page, and we're going to let the router basically handle the rendering of the component. We're not going to decide to render the SignInPage
component in here, but actually, we're just going to redirect and let React Router DOM
handle that for us. So let's come here to components. Let's make a new file and let's call this
one `ProtectedRoute.tsx`. And here I'm going to export default function,
protected route, and we're going to leave it empty for now. The first thing that we want to do is we need
to create some props for this component because it will need to have children, right? This component is going to wrap the HomePage
component and that's going to turn it into a protected route component. So we need to have children in this component. So we're going to come here and we're going
to do type `ProtectedRouteProps` equals `PropsWithChildren` and import this directly from React. This will basically allow you to have children
in any React component. And then we can come here and we can do children:
ReactNode. And then we can type our actual, let me see,
our actual props, `ProtectedRouteProps`, like so. And now to make the errors go away, we can
just return the children. And now we have the basic version of our protected
route component. Then what we can do is we can come back to
main and directly wrap the HomePage with `ProtectedRoute`. Protected Route imported and then paste the
HomePage and then save. So now if I open up the application and I
check here the URL, we are indeed on the index path, and we see here "Welcome," which is
coming directly from our HomePage component. I can refresh and we see "Welcome," everything
is working. If I now go to sign in, we're going to see
"Signed in" and our SignInPage component is also correctly being rendered. So now let's implement the logic of `ProtectedRoute`
to check the user to make sure that the user is signed in. And if the user isn't signed in, we redirect
to the sign-in page. So remember, in this `AuthProvider`, we have
this `useAuth` custom hook that we can use to easily access the user in this context. So that's what we're going to do. We're going to come here, make a new line,
and we're going to do `const user = useAuth()`, import this from `AuthProvider` and save like
so. If I now hover over this user, this user can
either be of type `User` or of type `null`. Because we have used this custom `useAuth`
hook, we've basically gotten rid of the undefined case. So we can just assume that we're either going
to have a user here or we're going to have null, which is basically what we set if `isSignedIn`
is `false`. The second thing that we need is we actually
need a way to navigate the user to a different page. And for that, we're going to be using the
`useNavigate` function from React Router DOM. So I'm going to come here and do `const navigate
= useNavigate()`, import this from React Router DOM directly. Now with this, we have everything that we
need to actually implement our logic. We're going to be doing all of this in a `useEffect`
because we want to basically make this check whenever this component mounts and then redirect
the user if the user isn't there. So we're going to do `useEffect`, import this
from React, we're going to set up our basic `useEffect` here, we're going to pass navigate
and user because we'll need both of these as dependencies of the effect. And then all that we have to do is just make
this check—and see, Copilot is being very helpful—if we don't have a user, we're going
to navigate to sign-in. Now, here's where you have to be really careful
when doing something like this. This code is actually not 100% correct. If you look at the type of `user` here, it
can either be `User` or `null`, right? Going back to the `AuthProvider`, we either
have a user or we have `null`. Here what we're doing is we're doing `!user`,
we're not checking for `null` directly. So with this code, if the user, for some reason,
ever ends up being `undefined`, `false`, `0`, or any other value that is falsey in JavaScript,
this code is going to run. We don't want this to run; we only want this
to run if the `user` is explicitly `null`, because that's how we configured it in this
component right here. So what we have to do is we have to make that
check explicitly. If `user === null`, then and only then do
we navigate to the sign-in page. And there's actually one more thing that we
have to do, we have to put in here as a second parameter, we have to give it `{ replace:
true }`. The reason why we want to do this, so first of all, `replace: true` here is going
to replace the entire history with this page. And this is going to prevent the user from
pressing back on the browser, and then going back to the page that they were on previously,
right? If the user isn't signed in, and this is a
protected route, we basically just want to send the user directly to sign-in and not
allow them to navigate to any other page. So now if I go back to the application, you're
going to see that already we are on the sign-in page. But even if I go in the URL and manually change
it back to index, I can go to index and it's automatically going to redirect me to the
sign-in page, I can press back and I'm not being sent back to the index page, I'm always
going to be on the sign-in page. This is how you build a simple protected route
component. And then you can wrap any component that you
want to have protected in your router, and you're going to get the same functionality. The benefit of doing it this way is that now
all of this code is extracted into a separate component, and it's shared across all of your
protected routes. If you ever need to make a change to the user—maybe
the user is no longer going to be `null`—or if you ever need to make a change to this
logic—maybe you want to actually check some property of a user—you can do it here in
one place. And then you can make sure that all of your
protected routes will have this updated. And also, by the way, using this pattern is
not only beneficial for protected routes, but you can actually create any sort of route
component and put anything you want in there, and have that automatically apply to all of
your routes. Maybe you want some custom styling, maybe
you want some custom functionality like logging something to the analytics on a page view,
whatever you want, you can create a custom component and then wrap every route with it,
and then have that shared behavior across all of your routes. If you enjoyed this video and you want to
see more videos just like these, maybe videos that are a little bit shorter that look at
a specific thing in React, be sure to leave me a comment down below. Also, leave a thumbs up to this video and
also subscribe if you haven't already, because it would mean the world to me. And also here, you have a different video
of mine that you can watch that YouTube seems to think that you're really going to enjoy. With that being said, my name has been Darius
Cosden. This is Cosden Solutions. Thank you so much for watching, and I will
see you in the next video. Ciao, ciao.