React Router / react-router-dom: modos y patrones reales
Si React te enseña a construir componentes, el router te enseña a convertir esos componentes en una aplicación navegable.
En proyectos reales, esto deja de ser opcional muy rápido:
- home
- detalle de producto
- dashboard
- login
- perfil
- 404
Sin router, acabas simulando navegación con estado manual. Mala idea.
Qué es React Router
React Router es la solución más habitual para manejar navegación en apps React.
La documentación oficial actual distingue tres modos de uso:
DeclarativeDataFramework
Para un curso de fundamentos, lo importante es entender bien los dos primeros. El tercero ya se acerca a usar React Router como framework completo.
1. Modo declarativo: el clásico
Es el enfoque que mucha gente asocia con react-router-dom.
Patrón típico:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/products/:id" element={<ProductDetail />} />
</Routes>
</BrowserRouter>
);
}
Cuándo usarlo
- quieres algo simple
- tu app es una SPA clásica
- ya resuelves los datos por tu cuenta con
fetch, React Query o similar - no necesitas loaders/actions del router
Lo que debes conocer aquí
BrowserRouterRoutesRouteLinkNavLinkuseNavigateuseParamsuseLocation
2. Modo Data con RouterProvider
Aquí el router no solo decide qué componente renderizar. También puede participar en carga de datos, acciones y manejo de errores por ruta.
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: 'about', element: <About /> },
{ path: 'products/:id', element: <ProductDetail /> },
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
Cuándo usarlo
- quieres una arquitectura de rutas más seria
- quieres loaders o actions por ruta
- quieres manejar errores a nivel de ruta
- te interesa crecer sin rehacer la base del routing
Idea importante
No es “otro router distinto”. Es React Router con una capa más potente.
3. Qué elegir en un curso fundamentals
Mi criterio sería este:
- aprende primero
BrowserRouter+Routes+Route - entiende después
RouterProvider - no saltes a loaders/actions si aún no entiendes rutas anidadas, params y navegación
Si no dominas lo básico, las APIs avanzadas solo te van a ocultar el problema real.
Enlaces y navegación
Link
Para navegar sin recargar la página:
import { Link } from 'react-router-dom';
<Link to="/about">Sobre nosotros</Link>
No uses <a href="/about"> para navegación interna en una SPA, salvo que busques una recarga completa.
NavLink
Útil para menús porque sabe si la ruta está activa:
import { NavLink } from 'react-router-dom';
<NavLink
to="/dashboard"
className={({ isActive }) => (isActive ? 'active' : '')}
>
Dashboard
</NavLink>
useNavigate
useNavigate no pinta un enlace en pantalla. Te da una función para cambiar de ruta desde JavaScript.
Eso se llama navegación programática: navegar como consecuencia de una acción o una lógica, no porque el usuario haya pulsado un enlace de navegación visible.
Ejemplos típicos:
- después de un login correcto
- después de guardar un formulario
- cuando cancelas y vuelves atrás
- cuando una condición obliga a redirigir
- cuando una acción del usuario no es un enlace semántico
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
const ok = await fakeLogin();
if (ok) {
navigate('/dashboard');
}
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Entrar</button>
</form>
);
}
Aquí no tendría sentido usar un Link, porque el usuario no debería ir al dashboard “sin más”. Primero debe pasar la lógica del login y solo después redirigir.
Entonces, ¿por qué no usar siempre useNavigate?
Porque Link y NavLink expresan mejor la intención cuando lo que tienes es navegación visible de la interfaz.
Si en la UI hay algo cuyo propósito es “ir a otra página”, normalmente debe ser un enlace, no un botón con onClick={() => navigate(...)}.
Mal patrón:
import { useNavigate } from 'react-router-dom';
function BadMenu() {
const navigate = useNavigate();
return <button onClick={() => navigate('/products')}>Productos</button>;
}
Mejor:
import { Link } from 'react-router-dom';
function GoodMenu() {
return <Link to="/products">Productos</Link>;
}
Por qué Link o NavLink suelen ser mejores para navegación visible
- son más semánticos: representan un enlace real
- son más mantenibles: el JSX deja claro que eso navega
- son mejores para accesibilidad y expectativas del usuario
NavLinkademás te da el estado activo sin lógica extra
En resumen:
Link: para navegación normal entre páginas o vistasNavLink: para navegación normal cuando además necesitas saber cuál está activauseNavigate: para redirecciones o navegación disparada por lógica
Casos buenos para useNavigate
- redirigir tras login o logout
- volver atrás con un botón “Cancelar”
- navegar tras crear, editar o borrar un recurso
- llevar al usuario a otra ruta si falta un dato o no tiene permisos
Casos donde NO debería ser tu primera opción
- menús principales
- listas de enlaces
- sidebars
- tabs que conceptualmente son navegación
También permite retroceder o avanzar en el historial:
navigate(-1);
navigate(1);
Y también reemplazar la entrada actual del historial:
navigate('/login', { replace: true });
replace: true es útil cuando no quieres que el usuario vuelva con “atrás” a una pantalla que ya no tiene sentido, por ejemplo tras cerrar sesión o después de una redirección automática.
Params de ruta
<Route path="/products/:id" element={<ProductDetail />} />
import { useParams } from 'react-router-dom';
function ProductDetail() {
const { id } = useParams();
return <h1>Producto {id}</h1>;
}
Úsalo cuando la URL identifica un recurso concreto.
Ejemplos:
/users/:id/posts/:slug/shop/:category
Query params
No forman parte del path, pero sí de la URL:
/products?search=monitor&page=2
import { useSearchParams } from 'react-router-dom';
function ProductsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const search = searchParams.get('search') ?? '';
function updateSearch(value) {
setSearchParams({ search: value, page: '1' });
}
return (
<>
<input value={search} onChange={(e) => updateSearch(e.target.value)} />
<p>Buscando: {search}</p>
</>
);
}
Úsalos para:
- filtros
- paginación
- ordenación
- estado compartible por URL
Rutas anidadas y Outlet
Este es uno de los conceptos más importantes.
import { Outlet } from 'react-router-dom';
function DashboardLayout() {
return (
<div>
<DashboardSidebar />
<main>
<Outlet />
</main>
</div>
);
}
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="settings" element={<Settings />} />
<Route path="billing" element={<Billing />} />
</Route>
Outlet marca el lugar donde se pintará la ruta hija.
Sin entender esto, es fácil acabar duplicando layouts o creando árboles de rutas difíciles de mantener.
Ruta índice y 404
Ruta índice
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
</Route>
La ruta índice es la ruta hija por defecto.
404
<Route path="*" element={<NotFound />} />
Es la captura final cuando ninguna otra ruta coincide.
Rutas protegidas
Patrón típico:
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ user, children }) {
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
}
<Route
path="/dashboard"
element={
<ProtectedRoute user={user}>
<Dashboard />
</ProtectedRoute>
}
/>
Esto es suficiente para entender la idea. En aplicaciones grandes luego se suele combinar con layouts protegidos, loaders o auth centralizada.
Estructura mental recomendada
Piensa el routing en tres niveles:
- Layouts generales
- Páginas
- Subpáginas anidadas
Ejemplo:
//products/products/:id/dashboard/dashboard/settings/dashboard/billing
Si el árbol de rutas está bien pensado, la app escala mejor y el código se entiende antes.
Errores comunes
- usar
<a>en lugar deLinkpara navegación interna - meter toda la configuración en un único archivo gigante
- no usar rutas anidadas cuando el layout es compartido
- guardar en estado cosas que deberían ir en la URL
- tratar params y query params como si fueran lo mismo
Regla práctica final
Si estás empezando:
- empieza con el modo declarativo
- domina
Link,Route,useNavigate,useParamsyOutlet - luego sube a
RouterProvidersi la app pide más estructura
Eso te da una progresión sana sin convertir el routing en otra fuente de caos.
Lo que ya dominas en React Fundamentals
Con este curso ya tienes una base sólida de React:
- componentes y JSX
- props y composición
- eventos y estado
- condicionales y listas
- efectos
- formularios y estado compartido
- routing con React Router
Eso ya te permite construir una SPA real sin ir a ciegas.
Qué sigue ahora
El siguiente paso natural es el Curso de React Intermedio/Avanzado, donde entramos en Context, useReducer, useRef, custom hooks, Suspense, transiciones y patrones de arquitectura.
Y si lo que quieres es una referencia rápida para repasar sintaxis, hooks y patrones sin releer todo el curso, tienes la Cheat Sheet completa de React.