wandres.dev
FILE ROUTING · rutas y layouts

Navegación: el enlace A, useNavigate y saltos programáticos

Hay tres puertas para navegar en Solid, cada una para una intención distinta. El componente A es el enlace declarativo para el usuario, con estado activo automático y navegación del lado del cliente sin recarga. useNavigate da la navegación imperativa desde código, tras enviar un formulario o superar una guarda. Y el componente Navigate es la redirección declarativa en el render. Los tres comparten la transición del router, que mantiene la página vieja visible mientras la nueva carga.

⏱ 16 min

Definir rutas es la mitad del trabajo; la otra mitad es moverse entre ellas. Solid ofrece tres puertas de navegación, y elegir la correcta es cuestión de intención. Para un enlace que el usuario pulsa está el componente A, que además sabe cuándo la ruta a la que apunta es la activa. Para navegar desde código —tras guardar un formulario, tras comprobar una sesión— está useNavigate. Y para redirigir de forma declarativa según una condición del render está Navigate. Las tres pasan por la misma navegación del lado del cliente, sin recargar la página entera.

🎯 Al terminar esta lección sabrás
  • Usar el componente A para enlaces del lado del cliente con estado activo automático.
  • Controlar la coincidencia exacta con la prop end y las clases activeClass e inactiveClass.
  • Navegar imperativamente con useNavigate, incluyendo saltos de historial y opciones.
  • Redirigir de forma declarativa con Navigate y evitar los errores clásicos de cada puerta.

El componente A: enlaces que conocen la ruta activa

A es el enlace de Solid, la alternativa al ancla cruda. La diferencia esencial es que A hace navegación del lado del cliente: intercepta el clic, cambia la ruta y actualiza solo lo que cambió, sin la recarga completa que provoca un ancla normal. Además aplica automáticamente una clase cuando su href coincide con la ruta actual —activeClass, que por defecto es active— y marca el enlace con aria-current="page" para accesibilidad.

import { A } from "@solidjs/router";

export default function Menu() {
  return (
    <nav>
      <A href="/" end>Inicio</A>
      <A href="/blog" activeClass="activo" inactiveClass="apagado">Blog</A>
    </nav>
  );
}

La prop end merece atención. Por defecto, A considera activo su enlace cuando el href es un prefijo de la ruta actual, de modo que /blog sigue activo en /blog/solid. Pero eso convertiría a / en activo en todas las rutas, porque / es prefijo de cualquier cosa; por eso al enlace de inicio se le pone end, que exige coincidencia exacta. Regla: usa end en enlaces raíz y en cualquier sitio donde no quieras el comportamiento de prefijo.

Prop de A Efecto
href destino de la navegación del lado del cliente
activeClass clase aplicada cuando coincide; por defecto active
inactiveClass clase aplicada cuando no coincide
end exige coincidencia exacta en vez de por prefijo
replace reemplaza la entrada de historial en vez de apilarla

Más allá de la clase activa, A acepta las opciones de navegación que esperarías: replace para no apilar historial, noScroll para no saltar al inicio de la página tras navegar, state para adjuntar datos a la entrada de historial, y los atributos nativos de un ancla como target. Bajo el capó sigue emitiendo un ancla real, accesible y con su href visible, de modo que abrir en pestaña nueva o copiar el enlace funcionan como el usuario espera; lo único que cambia es que el clic normal lo intercepta el router en vez del navegador.

useNavigate: navegar desde código

Cuando la navegación no nace de un clic sino de una consecuencia —un formulario que se guardó, una sesión que caducó—, necesitas navegar imperativamente. useNavigate devuelve una función navigate a la que le pasas un destino. Admite un número para moverte por el historial —navigate(-1) vuelve atrás— y un objeto de opciones con replace, state y scroll.

import { useNavigate } from "@solidjs/router";

export default function Formulario() {
  const navigate = useNavigate();
  const enviar = async (datos: FormData) => {
    await guardar(datos);
    navigate("/gracias", { replace: true });
  };
  return <form action={enviar}>...</form>;
}

Los destinos pueden ser absolutos —/gracias— o relativos a la ruta actual, y navigate acepta también un state que viaja con la navegación y luego lees con useLocation. Un patrón habitual: al enviar a login desde una guarda, guardas la ruta de origen en ese state para devolver al usuario exactamente adonde estaba una vez autenticado.

// Guardar la ruta de origen para volver tras el login
import { useNavigate, useLocation } from "@solidjs/router";

function useIrALogin() {
  const navigate = useNavigate();
  const location = useLocation();
  return () => navigate("/login", { state: { volverA: location.pathname } });
}
⚠️
navigate va en un manejador, nunca en el cuerpo del render

useNavigate se llama en la parte superior del componente —es un hook que lee el contexto del router—, pero la función navigate que devuelve solo debe invocarse dentro de un manejador de evento o de un efecto. Si la llamas directamente en el cuerpo del componente, navegarás en cada ejecución del render, provocando bucles. Y recuerda: tanto A como useNavigate deben vivir bajo un <Router>; fuera de su contexto no tienen a quién preguntar la ruta actual.

A veces la decisión de navegar es parte del render mismo: “si no hay sesión, redirige a login”. Para eso está el componente Navigate, que al renderizarse dispara la navegación. Es la forma declarativa de una guarda, sin efectos ni manejadores.

import { Navigate } from "@solidjs/router";
import { Show } from "solid-js";

export default function Privado(props) {
  return (
    <Show when={props.autenticado} fallback={<Navigate href="/login" />}>
      {props.children}
    </Show>
  );
}

En el servidor, dentro de acciones y consultas de SolidStart, existe además la función redirect, que devuelve una respuesta de redirección real; es la herramienta correcta para guardas que deben resolverse antes de enviar HTML. Pero para la redirección en el cliente durante el render, Navigate es la puerta directa.

Todo pasa por la misma transición

Las tres puertas comparten algo que conviene hacer explícito: ninguna recarga la página. A, navigate y Navigate disparan navegación del lado del cliente, y esa navegación va envuelta en la transición que el router aplica por defecto, la misma del nivel de transiciones. La página actual permanece visible mientras la ruta destino resuelve sus datos, y solo entonces se conmuta, sin parpadeo intermedio.

Elegir la puerta es, por tanto, una decisión de intención, no de mecanismo: por debajo obtienes el mismo comportamiento —transición, precarga al pasar el ratón sobre un A, conservación de la vista vieja— vengas de un enlace, de una llamada imperativa o de un redirect declarativo. Por eso puedes mezclarlas sin coste en una sola pantalla.

// Las tres puertas conviven en una pantalla
import { A, useNavigate, Navigate } from "@solidjs/router";
import { Show } from "solid-js";

function Pantalla(props) {
  const navigate = useNavigate();
  return (
    <Show when={props.sesion} fallback={<Navigate href="/login" />}>
      <A href="/inicio" end>Inicio</A>
      <button onClick={() => navigate("/perfil")}>Ir al perfil</button>
    </Show>
  );
}
flowchart TD
N[necesito navegar] --> Q{de donde nace}
Q -->|clic del usuario en un enlace| A[componente A con estado activo]
Q -->|consecuencia en codigo| NAV[useNavigate imperativo]
Q -->|condicion en el render| DEC[componente Navigate declarativo]
style A fill:#89b4fa,color:#11111b
style NAV fill:#f9e2af,color:#11111b
style DEC fill:#a6e3a1,color:#11111b
🔗

A: para el usuario

Enlace del lado del cliente con clase activa automática y aria-current. Usa end para coincidencia exacta.

⚙️

useNavigate: para el código

Navegación imperativa tras una acción. Acepta saltos de historial y opciones como replace y state.

🛡️

Navigate: para guardas

Redirección declarativa en el render. Ideal como fallback de un <Show> que protege una ruta.

Tres puertas, tres intenciones, una sola transición por debajo

La maestría en navegación no está en memorizar tres APIs, sino en ver que cada una responde a una intención distinta y que las tres desembocan en el mismo mecanismo. El componente A es para la navegación que el usuario provoca: es declarativo, vive en el JSX como parte de la interfaz, y por eso puede hacer algo que las otras dos no —conocerse a sí mismo, saber si su destino es la ruta activa y vestirse en consecuencia con activeClass y aria-current—. useNavigate es para la navegación que tu código decide: es imperativo, se dispara desde la lógica, tras el await de un guardado o la comprobación de un permiso, y por eso su forma es una función que llamas en un manejador, no un elemento que pintas. Navigate es para la navegación que el render concluye: es declarativo como A pero no espera un clic, sino que redirige por el mero hecho de renderizarse, lo que lo hace el instrumento natural de una guarda expresada como fallback. Reconocer estas tres intenciones —el usuario provoca, el código decide, el render concluye— te dice sin dudar qué puerta usar en cada situación. Y bajo las tres late la misma verdad que recorre todo el enrutado de Solid: ninguna recarga la página, todas hacen navegación del lado del cliente envuelta en la transición del router, que mantiene la vista actual visible mientras la siguiente carga sus datos. La barra de direcciones sigue siendo la fuente de la verdad; estas tres puertas son, sencillamente, las tres maneras honestas de escribir en ella según quién tomó la decisión de moverse.

⚔️ Elige la puerta correcta
  1. Construye un menú con A, pon end en el enlace de inicio y comprueba que solo se marca activo en /, no en las subrutas.
  2. Personaliza activeClass e inactiveClass y verifica en el inspector que el enlace activo lleva aria-current="page".
  3. En un formulario, usa useNavigate para ir a una página de gracias con { replace: true } y comprueba que el botón de atrás no regresa al formulario.
  4. Provoca a propósito una llamada a navigate en el cuerpo del render y observa el bucle; muévela a un manejador para arreglarlo.
  5. Protege una ruta con <Show> y un <Navigate href="/login" /> como fallback, y confirma que sin sesión redirige de forma declarativa.