En esta ocasión vamos a usar la librearía de react router dom para poder crear rutas protegidas en nuestra aplicación de React JS.
Cualquier tipo de feedback es bienvenido, gracias y espero disfrutes el articulo.🤗
⚠️ Nota: Es necesario que cuentes con nociones de React Router Dom.
Tabla de contenido
📌 Tecnologías a utilizar.
📌 Creando el proyecto.
📌 Primeros pasos.
📌 Creando las paginas.
📌 Integrando React Router.
📌 Agregando protección de rutas.
📌 Conclusión.
🔒 Tecnologías a utilizar.
- ▶️ React JS (version 18)
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ React Router Dom (version 6)
- ▶️ CSS vanilla (Los estilos los encuentras en el repositorio al final de este post)
🔒 Creando el proyecto.
Al proyecto le colocaremos el nombre de: protected-routes
(opcional, tu le puedes poner el nombre que gustes).
npm init vite@latest
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.
Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.
cd protected-routes
Luego instalamos las dependencias.
npm install
Después abrimos el proyecto en un editor de código (en mi caso VS code).
code .
🔒 Primeros pasos.
Vamos al archivo src/App.tsx
y borramos todo el contenido para crear un nuevo componente. Que por el momento solo renderizar un "hola mundo"
const App = () => {
return (
<>
<div>Hello world</div>
</>
)
}
export default App
Lo que sigue es crear un Layout, esto solo es con propósito de estética para la app, no es obligatorio.
🚨 Nota: Cada vez que creamos una nueva carpeta, también crearemos un archivo index.ts para agrupar y exportar todas las funciones y componentes de otros archivos que están dentro de la misma carpeta, y que dichas funciones puedan ser importadas a traves de una sola referencia a esto se le conoce como archivo barril.
Creamos la carpeta src/components
y dentro creamos el archivo Layout.tsx para agregar:
interface Props { children: JSX.Element | JSX.Element[] }
export const Layout = ({ children }: Props) => {
return (
<div>
<h1>Protected Routes with <br /> <span>React Router</span></h1>
{children}
<div className="logo">
<img src="https://cdn.svgporn.com/logos/react-router.svg" alt="react-router" />
</div>
</div>
)
}
Luego lo importamos en el archivo src/App.tsx
import { Layout } from "./components"
import { AppRouter } from "./routes"
const App = () => {
return (
<Layout>
</Layout>
)
}
export default App
Debería lucir así 👀
🔒 Creando las paginas.
Creamos la carpeta src/pages
y dentro creamos dos archivos:
- HomePage.tsx, que actuara como la pagina que queremos que sea privada, o sea que solo usuarios con alguna autenticación puedan visualizar dicha pagina, incluso si el usuario sabe la URL de la pagina, no debe de poder verla.
export const HomePage = () => {
return (
<div className="page">HomePage</div>
)
}
- LoginPage.txs, que actuara como la pagina publica que cualquiera que no tenga autenticación pueda ver, pero si el usuario ya tiene una autenticación entonces esta pagina no debe ser visible ni conociendo la URL.
export const LoginPage = () => {
return (
<div className="page">LoginPage</div>
)
}
Aun no vamos a importar en ningún lado estas dos paginas.
Cabe mencionar que no se realizara ningún método de autenticación o algo parecido.
🔒 Agregando React Router.
Primero tenemos que instalar react router dom:
npm install react-router-dom
Después creamos una carpeta src/routes
y creamos el archivo AppRouter.tsx.
Este archivo sera el root de nuestro router, de aquí se desencadenaran las demás rutas.
Creamos un componente funcional.
export const AppRouter = () => {
return ()
}
Tenemos que importar algunos componentes que nos ofrece react-router-dom.
BrowserRouter, este componente tiene que envolver toda nuestra aplicación ya que es un proveedor. Una opción también es colocarlo en el archivo
src/main.tsx
, pero en mi caso me gustaría ponerlo en este archivo AppRouter.tsx y como este es el root de las rutas entonces esto es lo que vamos a importar ensrc/App.tsx
Routes, este componente debe estar dentro del BrowserRouter, y es el contenedor de las rutas que maneja nuestra aplicación, y cada vez que la ubicación cambia, este componente busca entre todas sus rutas hijas, para encontrar una ruta que coincida con la ubicación y poder renderizar el elemento que expone esa ruta hija.
-
Route, este componente es donde se definirá la ruta (mediante la prop path) y el elemento a renderizar (mediante la prop element pasando un elemento JSX).
- Cuando colocamos un '*' en la prop path, significa que en caso de que ninguna ruta anterior conicidad con la ubicación, entonces podemos renderizar una pagina 404, pero en este caso, se manda al usuario a otra ubicación.
-
Navigate, este componente sirve para cambiar la ubicación actual cuando se renderiza.
- to, es la prop para colocar el path hacia donde quieres ir en tu aplicación.
- replace es la prop para quitar del historial de navegación la actual ruta, y colocar la nueva ruta a la que te diriges. O sea que cuando quieras volver atrás, esa ya no estará la anterior pagina.
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"
export const AppRouter = () => {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='login' element={<LoginPage />} />
<Route path='*' element={<Navigate to='/login' replace />} />
</Routes>
</BrowserRouter>
)
}
Listo, aquí tenemos
Y ahora necesitamos hacer cambios en el AppRouter.tsxor el momento nuestro root router.
Ahora vamos a colocarlo en src/App.tsx
import { Layout } from "./components"
import { AppRouter } from "./routes"
const App = () => {
return (
<Layout>
<AppRouter />
</Layout>
)
}
export default App
Notaras que va a renderizar el HomePage.tsx, esto es porque cuando entramos a nuestra app, por defecto el path / y coincide con la ruta que renderiza el HomePage.tsx
Y si en la barra de dirección colocas /login, entonces te muestra el LoginPage.tsx.
Pero si colocas otra cosa que no sea un simple / o /login, entonces por defecto seria * y renderiza el componente Navigate que te manda al /login.
🔒 Agregando protección de rutas.
Por defecto, puedes visualizar el HomePage.tsx pero no debería ser posible, ya que no tienes una "autenticación". Asi que vamos a convertir esta ruta en una ruta privada.
Vamos a crear un nuevo archivo en src/routes
nombrado PrivateRoutes.tsx
Y dentro vamos a crear una nuevo conjunto de rutas, que serán nuestras rutas privadas.
Para anidar rutas necesitamos tanto el contenedor de rutas que es el componente Routes y las rutas como tal que son el componente Route
Aquí irán todas las rutas que queremos que sean privadas. Solamente movemos la ruta que renderiza el HomePage a este componente.
Y ademas agregaremos una nueva ruta para mandar al usuario en caso de que el usuario ingrese una ruta que no exista en .nuestra app.
Con el comodín '*' en la prop
import { Navigate, Route, Routes } from 'react-router-dom';
import { HomePage } from '../pages';
export const PrivateRoutes = () => {
return (
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='*' element={<Navigate to='/' replace />} />
</Routes>
);
};
Y ahora necesitamos hacer cambios en el AppRouter.tsx
Vamos a simular la autenticación con una simple variable (aunque en una app real lo mas probable es que venga de un API), lo importante es que tengas acceso a algún valor que te indique si el usuario esta autenticado o no y puedas usar dicho valor en el AppRouter.tsx:
type Status = 'checking' | 'authenticated' | 'no-authenticated'
let status: Status = 'no-authenticated'
En el caso de la opción checking es cuando inicia la app y no sabes si el usuario esta autenticado o no. En ese caso puedes renderizar un loading.
if (status === 'checking') return <div className="loading">Checking credentials...</div>
Y ahora haremos una condición dentro del contenedor de Rutas, donde si el usuario esta autenticado entonces déjalo ver las rutas privadas y no muestres el login que en este punto sera innecesario ya que el usuario ya esta autenticado.
De lo contrario, no renderize el componente de rutas privadas y muestra el LoginPage
Y al final, después de la condición, seguimos manteniendo la ruta con el comodín [*] ya que si el usuario ingresa una ruta que no exista entonces, hay que mandarlo a alguna parte que si existe de nuestra app.
Nota que en la ruta donde usamos [] el path de la prop **to* es /login y no es simplemente login. Ya que si colocamos solo la palabra login entonces mantendrá la URL como esta y solo le adicionara /login, lo cual no queremos.
Por ejemplo, si estamos en http://localhost:5173/about y queremos mandar al usuario a login, y en el componente Navigate en la prop to solo colocamos login sin el / al inicio entonces la URL resultante seria http://localhost:5173/about/login pero nosotros queremos apuntar al root de nuestras rutas y es por eso que debemos colocar el símbolo / al inicio, para que te envié a la URL http://localhost:5173/login
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"
import { PrivateRoutes } from "./"
import { LoginPage } from '../pages';
type Status = 'checking' | 'authenticated' | 'no-authenticated'
let status: Status = 'no-authenticated'
export const AppRouter = () => {
if (status === 'checking') return <div className="loading">Checking credentials...</div>
return (
<BrowserRouter>
<Routes>
{
status === 'authenticated'
? <Route path="/*" element={<PrivateRoutes />} />
: <Route path="login" element={<LoginPage />} />
}
<Route path='*' element={<Navigate to='/login' replace />} />
</Routes>
</BrowserRouter>
)
}
También nota que el path de las rutas privadas es '/*' significa que cualquier ruta que coincida con el / entonces entra al componente PrivateRoutes.
Un ejemplo seria: tienes la ruta about/* y alguien coloca el en buscador about/me entonces eso seria permitido y accedería al elemento que renderiza esa ruta. Si quitaras el comodín [*] entonces la ruta about/me ya NO sera permitido.
Hasta aquí ya tendríamos nuestras rutas privadas, y una sola ruta "publica" pero que tal si tenemos mas rutas publicas como la de crear usuario, entonces debemos crear un nuevo componente llamado PublicRoutes.tsx
Y agregar las rutas que queremos que sean "publicas" o sea disponibles cuando el usuario no este autenticado.
import { Navigate, Route, Routes } from 'react-router-dom';
import { LoginPage } from '../pages';
export const PublicRoutes = () => {
return (
<Routes>
<Route path='login' element={<LoginPage />} />
<Route path='*' element={<Navigate to='/login' replace />} />
</Routes>
);
};
Y asi quedaría nuestra AppRouter.tsx.
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"
import { PrivateRoutes, PublicRoutes } from "./"
type Status = 'checking' | 'authenticated' | 'no-authenticated'
let status: Status = 'authenticated'
export const AppRouter = () => {
if (status === 'checking') return <div className="loading">Checking credentials...</div>
return (
<BrowserRouter>
<Routes>
{
status === 'authenticated'
? <Route path="/*" element={<PrivateRoutes />} />
: <Route path="/*" element={<PublicRoutes />} />
}
<Route path='*' element={<Navigate to='/login' replace />} />
</Routes>
</BrowserRouter>
)
}
Notaras que ambas rutas tienen el mismo path, pero no sera un problema ya que su existencia es condicional, o solo existirá una ruta siempre.
Por cierto, si necesitas que alguna ruta sea accedida con o sin autenticación entonces colócala en el AppRouter.tsx después de la condición.
Y asi tendríamos rutas tanto publicas y privadas. 🥳
🔒 Conclusión.
Las rutas privadas son de mucha relevancia hoy en dia en las aplicaciones. Y con ayuda de React Router Dom nos hace mucho mas fácil la implementación de dicho proceso.
Otra idea que te puedo dar es que en las rutas privadas puedes anidar otras rutas para dividirlos por rol por ejemplo, si un usuario tiene el rol de 'user' no podrá ver la pagina de Admin, pero si tiene el rol de 'admin' entonces si podrá ver dicha pagina.
Espero que te haya gustado esta publicación y que te haya ayudada a entender más sobre como usar React Router Dom para implementar la protección de rutas. 🤗
Esta forma de hacer rutas privadas, no es la única, si es que conoces alguna otra forma distinta o mejor de realizar esta funcionalidad con gusto puedes comentarla 🙌.
Te invito a que revises mi portafolio en caso de que estés interesado en contactarme para algún proyecto! Franklin Martinez Lucas
🔵 No olvides seguirme también en twitter: @Frankomtz361
🔓 Código fuente.
Franklin361 / protected-routes
Create protected routes with React and React Router Dom 🔒
Protección de rutas con React Router Dom. 💪
This time, we are going to create protected routes with React Router Dom!
Features ⚙️
- Protected Routes.
- Show Public Routes.
- Redirecting.
Technologies 🧪
-
▶️ React JS (v 18) -
▶️ Vite JS -
▶️ TypeScript -
▶️ React Router Dom -
▶️ CSS vanilla
Installation 🧰
- Clone the repository (you need to have Git installed).
git clone https://github.com/Franklin361/protected-routes
- Install dependencies of the project.
npm install
- Run the project.
npm run dev
Top comments (2)
Genial, esto me ayuda mucho con un proyecto que tengo, gracias bro!
Muy buena explicación, muy útil, gracias por tu blog