¡Ven a la JSConf España 2026! Comprar entradas

Rutas protegidas con React Router y estado global

Hasta ahora hemos creado múltiples páginas con React Router y ya contamos con un estado global para manejar la sesión del usuario. Sin embargo, hay un problema importante: cualquier usuario puede acceder a páginas que deberían estar restringidas, como el perfil, incluso sin haber iniciado sesión.

En esta clase vamos a solucionar esto creando rutas protegidas, combinando React Router con el estado global para controlar el acceso de forma clara y reutilizable.

El problema de las rutas sin protección

Imagina una página /profile que muestra información del usuario.

Si el usuario no ha iniciado sesión:

  • No debería poder acceder a esa página.
  • No tiene sentido mostrar un perfil vacío o inexistente.
  • Deberíamos redirigirlo automáticamente a /login.

Sin protección, React Router renderiza la página igualmente si el usuario accede directamente a la URL.

La idea de una ruta protegida

Una ruta protegida es un componente que:

  • Comprueba si el usuario tiene sesión iniciada.
  • Si no la tiene, redirige a otra ruta (por ejemplo /login).
  • Si sí la tiene, renderiza el contenido protegido.

Para esto vamos a crear un componente intermedio que actúe como guardián de la ruta.

Creando el componente ProtectedRoute

Creamos un componente pequeño y reutilizable, por ejemplo ProtectedRoute.jsx.

Este componente:

  • Lee el estado global para saber si el usuario está autenticado.
  • Usa Navigate de React Router para redirigir si no hay sesión.
  • Renderiza children si todo está correcto.

Conceptualmente hace esto:

¿Hay sesión?

Sí  → renderiza la página
No  → redirige a /login

Un ejemplo típico:

import { Navigate } from 'react-router-dom'
import { useAuthStore } from '../store/auth'

export function ProtectedRoute({ children }) {
  const isLoggedIn = useAuthStore((state) => state.isLoggedIn)

  if (!isLoggedIn) {
    return <Navigate to="/login" replace />
  }

  return children
}

Integración con React Router

React Router no permite envolver rutas directamente como si fueran componentes normales. Por eso, ProtectedRoute debe usarse dentro de la propiedad element de la ruta.

En lugar de renderizar directamente la página:

  • Envolvemos la página dentro de ProtectedRoute.
  • React Router evalúa primero la protección.
  • Solo renderiza la página si el usuario cumple las condiciones.

Un ejemplo:

import { Route, Routes } from 'react-router-dom'
import { ProtectedRoute } from './ProtectedRoute'
import { ProfilePage } from './pages/ProfilePage'
import { LoginPage } from './pages/LoginPage'

export function AppRoutes() {
  return (
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route
        path="/profile"
        element={
          <ProtectedRoute>
            <ProfilePage />
          </ProtectedRoute>
        }
      />
    </Routes>
  )
}

Esto puede parecer un poco rígido, pero es la forma correcta de hacerlo con React Router.

Protegiendo la página de perfil

Aplicamos ProtectedRoute a la ruta /profile.

Ahora ocurre lo siguiente:

Usuario sin sesión

  • Intenta acceder a /profile.
  • Es redirigido automáticamente a /login.

Usuario con sesión

  • Accede a /profile.
  • Ve su información correctamente.

Además, si el usuario cierra sesión estando en /profile, el sistema lo redirige de nuevo a login automáticamente (porque el estado global cambia y la protección se re-evalúa).

Login y register como rutas públicas

Las páginas de login y registro:

  • No deben estar protegidas.
  • Se renderizan libremente.
  • Al iniciar sesión, actualizan el estado global.
  • Tras el login, pueden redirigir a una ruta privada como /profile.

En esta fase el login está mockeado, pero la estructura ya es válida para conectar con un backend real más adelante.

Qué hemos conseguido hasta ahora

Con esta implementación:

  • Ya tenemos navegación entre páginas.
  • Hemos aprendido a proteger rutas sensibles.
  • El acceso depende del estado global del usuario.
  • La aplicación empieza a comportarse como una app real.

Además, todo esto es escalable. Podemos proteger tantas rutas como queramos reutilizando el mismo componente.

Qué viene a continuación

Hasta ahora todo el estado está en frontend y simulado. En las próximas clases nos centraremos en:

  • Crear el backend real.
  • Autenticación con servidor.
  • Persistencia en base de datos.
  • APIs, SQL y validación real de usuarios.

Con esto cerramos la parte de Estado Global y React Router, dejando una base sólida para conectar todo con backend.