4-Navegación

por Ariel GonzAgüer

última actualización: 26-8-2026


Link

Este es el componente que debemos usar para crear enlaces de navegación interna. Acepta una prop `to` que indica la ruta a la que se quiere navegar, y que se obtiene automáticamente antes de la navegación.

En este proyecto, podemos ver el uso de `<Link>` en el header para navegar entre las diferentes páginas:

1// src/components/Header.tsx
2import { Link } from "waku"
3export default function Header() {
4
5 return (
6 <header className="bg-black text-white h-10 flex items-center justify-center">
7 <nav className="w-full flex items-center justify-between gap-10 px-4 max-w-4xl mx-auto">
8 <Link to="/temas" className="hover:text-amber-300! transition-all duration-300">Temas</Link>
9 <Link to="/" className="hover:text-amber-300! transition-all duration-300">Inicio</Link>
10 <Link to="/acerca-de" className="hover:text-amber-300! transition-all duration-300">Acerca De</Link>
11 </nav>
12 </header>
13 )
14};
15

useRouter

El hook `useRouter` se puede usar para inspeccionar la ruta actual y realizar navegaciones programáticas.

Propiedades del router

El objeto router tiene dos propiedades relacionadas a la ruta actual:

  • path: tipo string.
  • query: tipo string.

Métodos del router

El objeto enrutador también contiene varios métodos para la navegación programática:

  • router.push(to: string) - Navegar a la ruta proporcionada
  • router.prefetch(to: string) - Precargar la ruta proporcionada
  • router.replace(to: string) - Reemplazar la entrada actual del historial
  • router.reload() - Recargar la ruta actual
  • router.back() - Navegar a la entrada anterior en el historial de sesiones
  • router.forward() - Navegar a la siguiente entrada en el historial de sesiones

Acá un ejemplo de uso de `useRouter`, donde vemos `path`, `query`, `router.push()` y `router.reload()`:

1// src/components/Router.tsx
2"use client";
3
4import { useRouter } from "waku";
5
6export default function Router() {
7 const { path, query } = useRouter();
8 const router = useRouter();
9
10 return (
11 <section
12 className="flex flex-col justify-center items-center my-10 text-xl"
13 >
14 <p className="mb-6">Ruta actual: {path}</p>
15 <p className="mb-6">Parámetros de consulta: {JSON.stringify(query)}</p>
16
17 <button
18 className="text-black py-5 px-4 mb-4 border-2 border-red-600 bg-amber-400 rounded"
19 onClick={() => router.push("/temas/4-navegacion")}
20 >
21 Ir al tema 4-navegación
22 </button>
23 <button
24 className="text-black py-5 px-4 border-2 border-red-600 bg-amber-400 rounded"
25 onClick={() => {
26 alert("Recargando...");
27 router.reload();
28 }}
29 >
30 Recargar
31 </button>
32 </section>
33 );
34};
35

Puede ver la página que usa este componente aquí acá → página que usa el componente Router.

Nota: al ser un hook, solo podemos usarlo en Componentes de Cliente.

Navegación tipada (Typed Routes)

Desde Waku 1.0, las APIs de navegación aceptan objetivos tipados. Al navegar a rutas dinámicas, los parámetros se verifican en tiempo de compilación:

1// Navegación tipada con router.push
2"use client";
3
4import { useRouter } from "waku";
5
6export const PostButton = ({ slug }: { slug: string }) => {
7 const router = useRouter();
8
9 return (
10 <button
11 onClick={() =>
12 router.push({
13 to: "/posts/[slug]",
14 params: { slug },
15 hash: "top",
16 })
17 }
18 >
19 Abrir post
20 </button>
21 );
22};
23

Las propiedades del objetivo tipado son:

  • to: el patrón de ruta (ej: /posts/[slug], /docs/[...path]).
  • params: requerido cuando el patrón tiene segmentos dinámicos, tipado desde el patrón.
  • hash (opcional): fragmento de la URL.
  • search: parámetros de búsqueda tipados por ruta mediante un "search codec".

La forma de string plano sigue funcionando: router.push('/posts/hello').

Navegación instantánea (Instant Navigation)

Waku puede cachear las partes estáticas de una ruta y mostrarlas inmediatamente al navegar, mientras las partes dinámicas se cargan en segundo plano. Esto se logra con la prop experimental unstable_instant en <Link>:

1// Navegación instantánea con <Link>
2import { Suspense } from "react";
3import type { ReactNode } from "react";
4import { Link } from "waku";
5
6export default function Layout({ children }: { children: ReactNode }) {
7 return (
8 <div>
9 <nav>
10 <Link to="/posts/1" unstable_instant>
11 Post 1
12 </Link>
13 <Link to="/posts/2" unstable_instant>
14 Post 2
15 </Link>
16 </nav>
17 <Suspense fallback={<p>Cargando...</p>}>
18 {children}
19 </Suspense>
20 </div>
21 );
22}
23

Para que funcione, se necesita:

  • → Que el shell estático de la ruta ya esté en caché (por una visita anterior o un prefetch).
  • → Que la parte dinámica de la página esté dentro de un componente <Suspense>.

Cuando el shell está en caché, el layout y el fallback de <Suspense> aparecen sin espera, y el contenido dinámico se transmite una vez que el servidor responde. Si el shell no está en caché, la navegación se comporta normalmente (espera la respuesta del servidor).