En esta página
React Router v7
React Router v7 — el estándar para SPA
React Router es la librería de enrutamiento más usada en el ecosistema React. La versión 7 introdujo el concepto de rutas como unidades con loaders y actions, acercándolo al modelo de Remix.
Instalación
npm install react-router-domcreateBrowserRouter — la API moderna
La API recomendada desde React Router v6.4 es createBrowserRouter con la configuración de rutas como array de objetos:
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Inicio /> },
{ path: 'blog', element: <Blog /> },
{ path: 'blog/:slug', element: <Articulo /> },
],
},
]);
function main() {
ReactDOM.createRoot(document.getElementById('root')!).render(
<RouterProvider router={router} />
);
}Link y NavLink
import { Link, NavLink } from 'react-router-dom';
function Navegacion() {
return (
<nav>
{/* Link básico: navegación sin recarga */}
<Link to="/">Inicio</Link>
{/* NavLink: sabe si está activo */}
<NavLink
to="/cursos"
className={({ isActive, isPending }) =>
isActive ? 'nav-activo' : isPending ? 'nav-cargando' : ''
}
>
Cursos
</NavLink>
{/* Link con estado (accesible vía useLocation) */}
<Link to="/detalle/1" state={{ desde: 'lista' }}>
Ver detalle
</Link>
</nav>
);
}Parámetros de ruta y navegación programática
import { useParams, useNavigate, useSearchParams, useLocation } from 'react-router-dom';
function DetalleProducto(): React.JSX.Element {
// Parámetros de la URL: /productos/:categoria/:id
const { categoria, id } = useParams<{ categoria: string; id: string }>();
// Navegación programática
const navegar = useNavigate();
// Query params: /productos?pagina=2&orden=precio
const [searchParams, setSearchParams] = useSearchParams();
const pagina = Number(searchParams.get('pagina') ?? '1');
// Estado de la ubicación actual
const ubicacion = useLocation();
const irPaginaSiguiente = () => {
setSearchParams({ pagina: String(pagina + 1), orden: searchParams.get('orden') ?? 'fecha' });
};
return (
<div>
<p>Categoría: {categoria} | ID: {id}</p>
<p>Página actual: {pagina}</p>
<button type="button" onClick={() => navegar(-1)}>← Atrás</button>
<button type="button" onClick={() => navegar('/carrito')}>Ir al carrito</button>
<button type="button" onClick={irPaginaSiguiente}>Siguiente página</button>
<p>Veniste desde: {String(ubicacion.state as { desde?: string } | null)?.toString()}</p>
</div>
);
}Layouts anidados y Outlet
Outlet es el marcador donde React Router renderiza la ruta hija activa:
function LayoutDashboard(): React.JSX.Element {
return (
<div className="dashboard">
<aside>
<nav>
<NavLink to="/dashboard">Resumen</NavLink>
<NavLink to="/dashboard/configuracion">Configuración</NavLink>
<NavLink to="/dashboard/facturacion">Facturación</NavLink>
</nav>
</aside>
<main>
<Outlet /> {/* Aquí se renderiza la sub-ruta activa */}
</main>
</div>
);
}
// Configuración del router con layout anidado
{
path: 'dashboard',
element: <LayoutDashboard />,
children: [
{ index: true, element: <ResumenDashboard /> },
{ path: 'configuracion', element: <Configuracion /> },
{ path: 'facturacion', element: <Facturacion /> },
],
}Loaders — datos antes de renderizar
Los loaders son funciones async que se ejecutan antes del render:
import { useLoaderData, type LoaderFunctionArgs } from 'react-router-dom';
interface Articulo {
id: string;
titulo: string;
contenido: string;
autor: string;
}
// El loader se ejecuta antes de montar el componente
async function articuloLoader({ params }: LoaderFunctionArgs): Promise<Articulo> {
const res = await fetch(`/api/articulos/${params.slug}`);
if (!res.ok) throw new Response('No encontrado', { status: 404 });
return res.json() as Promise<Articulo>;
}
function PaginaArticulo(): React.JSX.Element {
// Los datos ya están disponibles — sin estado de carga
const articulo = useLoaderData() as Articulo;
return (
<article>
<h1>{articulo.titulo}</h1>
<p>Por: {articulo.autor}</p>
<div dangerouslySetInnerHTML={{ __html: articulo.contenido }} />
</article>
);
}
// Configuración con loader
{ path: 'blog/:slug', loader: articuloLoader, element: <PaginaArticulo /> }Manejo de errores en rutas
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';
function ErrorBoundaryRuta(): React.JSX.Element {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>{error.status} — {error.statusText}</h1>
<p>{error.data as string}</p>
</div>
);
}
return <h1>Error inesperado</h1>;
}
// Asignar errorElement a rutas específicas o al root
{ path: '/', element: <Layout />, errorElement: <ErrorBoundaryRuta /> }Rutas lazy — carga perezosa
import { lazy, Suspense } from 'react';
const PanelAdmin = lazy(() => import('./pages/PanelAdmin'));
// En el router
{
path: 'admin',
element: (
<Suspense fallback={<p>Cargando panel…</p>}>
<PanelAdmin />
</Suspense>
),
}La carga perezosa divide el bundle y carga el código del panel de admin solo cuando el usuario navega a esa ruta, reduciendo el tiempo de carga inicial de la aplicación.
React Router v7 unifica framework y SPA modes
React Router v7 fusionó Remix y React Router. En modo SPA (createBrowserRouter) funciona como siempre. En modo framework (con el adaptador) agrega SSR, loaders del servidor y más. Este curso usa el modo SPA que es el más común para aplicaciones React puras.
Usa Link en vez de anchor tags para navegación interna
Nunca uses <a href=...> para navegar entre rutas internas: recargará la página completa. Usa siempre <Link to=...> para navegación SPA (sin recarga) y <NavLink> cuando necesites detectar si la ruta está activa para aplicar estilos.
Los loaders se ejecutan antes de renderizar el componente
Los loaders de React Router v7 son funciones async que se ejecutan antes de que el componente se monte. Lanza errores (throw) o redirige (redirect) desde el loader para manejar errores y autenticación. Usa useLoaderData() tipado para consumir los datos en el componente.
Inicia sesión para guardar tu progreso