DEV Community

Cover image for Protección de rutas con React Router Dom. 💪
Franklin Martinez
Franklin Martinez

Posted on

Protección de rutas con React Router Dom. 💪

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.

📌 Código fuente.

 

🔒 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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Luego instalamos las dependencias.



npm install


Enter fullscreen mode Exit fullscreen mode

Después abrimos el proyecto en un editor de código (en mi caso VS code).



code .


Enter fullscreen mode Exit fullscreen mode

🔒 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


Enter fullscreen mode Exit fullscreen mode

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>
    )
}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Debería lucir así 👀

first

🔒 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>
    )
}


Enter fullscreen mode Exit fullscreen mode
  • 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>
    )
}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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 en src/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>
    )
}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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

home

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


Enter fullscreen mode Exit fullscreen mode

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'


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

loading

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>
    )
}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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>
    )
}


Enter fullscreen mode Exit fullscreen mode

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.

GitHub logo 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!

demo

 

Features ⚙️

  1. Protected Routes.
  2. Show Public Routes.
  3. Redirecting.

 

Technologies 🧪

  • ▶️ React JS (v 18)
  • ▶️ Vite JS
  • ▶️ TypeScript
  • ▶️ React Router Dom
  • ▶️ CSS vanilla

 

Installation 🧰

  1. Clone the repository (you need to have Git installed).
    git clone https://github.com/Franklin361/protected-routes
Enter fullscreen mode Exit fullscreen mode
  1. Install dependencies of the project.
    npm install
Enter fullscreen mode Exit fullscreen mode
  1. Run the project.
    npm run dev
Enter fullscreen mode Exit fullscreen mode

 

Article links ⛓️

Here's the link to the tutorial in case you'd like to take a look at it! eyes 👀






Top comments (2)

Collapse
 
flash010603 profile image
Usuario163

Genial, esto me ayuda mucho con un proyecto que tengo, gracias bro!

Collapse
 
lufc profile image
Luis Felipe CE

Muy buena explicación, muy útil, gracias por tu blog