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.
Inicia sesión para guardar tu progreso