React Router v7 con TypeScript
React Router es una biblioteca popular para manejar el enrutamiento en aplicaciones React. En esta guía, veremos cómo configurar y usar React Router v7 con TypeScript.
Modos de React Router
React Router ofrece tres modos principales para integrarlo en tu aplicación. Estos modos son aditivos: cada uno añade funcionalidades sobre el anterior, lo que implica también un mayor compromiso con la arquitectura del proyecto.
La elección depende de cuánto control necesitas y cuánta ayuda quieres recibir de React Router.
🟢 1. Modo Declarativo (Declarative Mode)
El modo declarativo habilita funciones de enrutamiento básicas, como hacer coincidir URL con componentes, navegar por la aplicación y proporcionar estados activos con API como <Link>
, useNavigate
, useLocation
.
import { BrowserRouter } from "react-router";
ReactDOM.createRoot(root).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
🟡 2. Modo de Datos (Data Mode)
Añade funcionalidades para manejo de datos, separando la configuración de rutas del renderizado.
Características:
Permite definir:
loader
(carga de datos),action
(envío de datos),useFetcher
(gestión de estados pendientes).
Mejora la experiencia del usuario con datos precargados.
Uso recomendado si:
- Quieres un mayor control sobre datos, bundling y abstracciones del servidor.
- Estás usando el enrutador de datos desde React Router v6.4.
Configuración:
import { createBrowserRouter, RouterProvider } from "react-router";
let router = createBrowserRouter([
{
path: "/",
Component: Root,
loader: loadRootData,
},
]);
ReactDOM.createRoot(root).render(<RouterProvider router={router} />);
🔵 3. Modo Framework (Framework Mode)
La experiencia más completa, ideal si quieres aprovechar todo lo que ofrece React Router.
Características:
Basado en el Modo de Datos, pero con integración total con herramientas como Vite.
Ofrece:
Seguridad de tipos en
href
y módulos de ruta.División inteligente de código.
Estrategias de renderizado:
- SPA (Single Page Application),
- SSR (Server Side Rendering),
- SSG (Static Site Generation).
Uso recomendado si:
- Eres nuevo en React Router y no tienes una arquitectura definida.
- Estás migrando desde frameworks como Next.js o Remix.
- Buscas una alternativa a SvelteKit o simplemente quieres "construir con React".
Configuración:
routes.ts
import { index, route } from "@react-router/dev/routes";
export default [index("./home.tsx"), route("products/:pid", "./product.tsx")];
Luego tendrá acceso a la API del módulo de ruta con parámetros de tipo seguro, loaderData, división de código, estrategias SPA/SSR/SSG y más.
product.tsx
import { Route } from "+./types/product.tsx";
export async function loader({ params }: Route.LoaderArgs) {
let product = await getProduct(params.pid);
return { product };
}
export default function Product({ loaderData }: Route.ComponentProps) {
return <div>{loaderData.product.name}</div>;
}
Modo Declarativo
npx create-vite@latest my-app
cd my-app
npm i react-router
main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { BrowserRouter } from "react-router";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
- BrowserRouter: Componente que envuelve tu aplicación y habilita el enrutamiento basado en la URL del navegador.
- Routes: Componente que agrupa todas las rutas de tu aplicación.
- Route: Componente que define una ruta específica.
- path: es la ruta de la URL que se va a coincidir.
- element: es el componente que se renderizará cuando la ruta coincida.
App.tsx
import { Route, Routes } from "react-router";
const App = () => {
return (
<Routes>
<Route
path="/"
element={<h1>Home Page</h1>}
/>
{/* <Route
index
element={<Home />}
/> */}
<Route
path="about"
element={<h1>About Page</h1>}
/>
</Routes>
);
};
export default App;
Layouts
Los layouts son componentes que se utilizan para envolver otras rutas y proporcionar una estructura común a varias páginas.
src\pages\auth\auth-layout.tsx
import { Outlet } from "react-router";
const AuthLayout = () => {
return (
<div>
<Outlet />
<footer>
<p>Auth Layout Footer</p>
</footer>
</div>
);
};
export default AuthLayout;
App.tsx
import { Route, Routes } from "react-router";
import AuthLayout from "./pages/auth/auth-layout";
import Login from "./pages/auth/login";
import Register from "./pages/auth/register";
const App = () => {
return (
<Routes>
...
<Route element={<AuthLayout />}>
<Route
path="login"
element={<Login />}
/>
<Route
path="register"
element={<Login />}
/>
</Route>
</Routes>
);
};
export default App;
Rutas Anidadas
Las rutas anidadas permiten crear una jerarquía de rutas, donde una ruta puede contener otras rutas dentro de ella.
- useLocation: Hook que permite acceder a la ubicación actual de la URL.
src\pages\admin\admin-layout.tsx
import { Outlet, useLocation } from "react-router";
const AdminLayout = () => {
const location = useLocation();
return (
<div>
<header>
<p>Current Path: {location.pathname}</p>
</header>
<Outlet />
<footer>
<p>Admin Footer</p>
</footer>
</div>
);
};
export default AdminLayout;
App.tsx
import { Route, Routes } from "react-router";
import AdminLayout from "./pages/admin/admin-layout";
import Dashboard from "./pages/admin/dashboard";
import Profile from "./pages/admin/profile";
const App = () => {
return (
<Routes>
...
<Route
path="admin"
element={<AdminLayout />}
>
<Route
index
element={<Dashboard />}
/>
<Route
path="profile"
element={<Profile />}
/>
</Route>
</Routes>
);
};
export default App;
Rutas Dinámicas
Las rutas dinámicas permiten crear rutas que pueden cambiar según los parámetros de la URL.
src\pages\admin\product.tsx
import { useParams } from "react-router";
const Product = () => {
const params = useParams();
console.log({ params });
return (
<div>
<h2>Product Page</h2>
<p>Product ID: {params.id}</p>
<pre>{JSON.stringify(params, null, 2)}</pre>
</div>
);
};
export default Product;
App.tsx
<Route
path="admin"
element={<AdminLayout />}
>
...
<Route
path="products/:id"
element={<Product />}
/>
</Route>
Varios Parámetros
<Route
path="products/:name/:id"
element={<Product />}
/>
Parámetros Opcionales
<Route
path="products/:name/:id?"
element={<Product />}
/>
Splats
Los splats permiten capturar partes de la URL que no coinciden con ninguna ruta específica.
- Link: Componente que permite crear enlaces a otras rutas. La diferencia con un enlace HTML es que
Link
no recarga la página, sino que actualiza la URL y renderiza el componente correspondiente.
src\pages\not-found.tsx
import { Link } from "react-router";
const NotFound = () => {
return (
<div>
<h2>Page Not Found</h2>
<p>Please check the URL or return to the home page.</p>
<Link to="/">Go to Home</Link>
</div>
);
};
export default NotFound;
App.tsx
import { Route, Routes } from "react-router";
import NotFound from "./pages/not-found";
const App = () => {
return (
<Routes>
...
<Route
path="*"
element={<NotFound />}
/>
</Routes>
);
};
export default App;
Navegación
Para navegar entre rutas, puedes usar Link
, NavLink
y useNavigate
.
NavLink
NavLink
es similar a Link
, pero permite aplicar estilos activos a los enlaces.
- end: Propiedad que indica si el enlace debe coincidir exactamente con la ruta.
src\components\admin-navbar.tsx
import { NavLink } from "react-router";
const AdminNavbar = () => {
return (
<nav>
<ul>
<li>
<NavLink
to="/admin"
end
>
Dashboard
</NavLink>
</li>
<li>
<NavLink to="/admin/profile">Profile</NavLink>
</li>
<li>
<NavLink to="/admin/products/product1/123">Product 1</NavLink>
</li>
</ul>
</nav>
);
};
export default AdminNavbar;
También puedes aplicar estilos activos a los enlaces utilizando la clase active
:
src\index.css
a.active {
color: red;
}
src\pages\admin\admin-layout.tsx
import { Outlet, useLocation } from "react-router";
import AdminNavbar from "../../components/admin-navbar";
const AdminLayout = () => {
const location = useLocation();
return (
<div>
<header>
<p>Current Path: {location.pathname}</p>
</header>
<AdminNavbar />
<Outlet />
<footer>
<p>Admin Footer</p>
</footer>
</div>
);
};
export default AdminLayout;
isActive
Puedes usar la función isActive
para aplicar estilos condicionales a los enlaces.
src\components\admin-navbar.tsx
import { NavLink } from "react-router";
const AdminNavbar = () => {
return (
<nav>
<ul className="nav nav-underline">
<li className="nav-item">
<NavLink
to="/admin"
end
className={({ isActive }) =>
`nav-link p-2 ${
isActive ? "active bg-secondary" : "text-secondary"
}`
}
style={({ isActive }) => ({ color: isActive ? "salmon" : "black" })}
>
Dashboard
</NavLink>
</li>
<li className="nav-item">
<NavLink
to="/admin/profile"
className={({ isActive }) =>
`nav-link p-2 ${
isActive ? "active bg-secondary" : "text-secondary"
}`
}
style={({ isActive }) => ({ color: isActive ? "salmon" : "black" })}
>
Profile
</NavLink>
</li>
<li className="nav-item">
<NavLink
to="/admin/products/product1/123"
className={({ isActive }) =>
`nav-link p-2 ${
isActive ? "active bg-secondary" : "text-secondary"
}`
}
style={({ isActive }) => ({ color: isActive ? "salmon" : "black" })}
>
Product 1
</NavLink>
</li>
</ul>
</nav>
);
};
export default AdminNavbar;
Modo mejorado:
import { NavLink } from "react-router";
// Define cada ítem del menú
type NavItem = {
to: string;
label: string;
end?: boolean;
};
const navItems: NavItem[] = [
{ to: "/admin", label: "Dashboard", end: true },
{ to: "/admin/profile", label: "Profile" },
{ to: "/admin/products/product1/123", label: "Product 1" },
];
// Funciones utilitarias para clases y estilos
const getNavClass = (isActive: boolean) =>
`nav-link p-2 ${isActive ? "active bg-secondary" : "text-secondary"}`;
const getNavStyle = (isActive: boolean) => ({
color: isActive ? "salmon" : "black",
});
const AdminNavbar = () => {
return (
<nav>
<ul className="nav nav-underline">
{navItems.map(({ to, label, end }, index) => (
<li
className="nav-item"
key={index}
>
<NavLink
to={to}
end={end}
className={({ isActive }) => getNavClass(isActive)}
style={({ isActive }) => getNavStyle(isActive)}
>
{label}
</NavLink>
</li>
))}
</ul>
</nav>
);
};
export default AdminNavbar;
useNavigate
useNavigate
es un hook que permite programáticamente navegar a otras rutas.
src\pages\admin\dashboard.tsx
import { useNavigate } from "react-router";
const Dashboard = () => {
const navigate = useNavigate();
return (
<div>
<h1>Admin Dashboard</h1>
<p>Welcome to the admin dashboard!</p>
<button
onClick={() => navigate("/admin/profile")}
className="btn btn-primary"
>
Go to Profile
</button>
</div>
);
};
export default Dashboard;