# React Router: navigating from code without breaking the router

Where you are decides the call: `useNavigate` in a component, `redirect` in a loader, the router instance outside React for anything else.

> Page: https://ui.eneawork.it/blog/react-router-navigare-programmaticamente
> Markdown: https://ui.eneawork.it/blog/react-router-navigare-programmaticamente.md

*2026-11-01*

Risposta breve: dentro un componente usa useNavigate() e chiamalo in un evento o in un effetto; in un loader usa redirect(); fuori da React usa l'istanza del router creata con createBrowserRouter. Mai navigate() durante il render.

La domanda «How can I programmatically navigate using React Router?» vale 2376 voti su Stack Overflow (letto il 31 ottobre 2026 con la Stack Exchange API): fonte. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi, 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026. Ogni comando qui sotto è eseguibile in locale: nessun passaggio richiede un account.

## Il meccanismo: la navigazione è uno stato, non una chiamata

Il router tiene una location e un insieme di sottoscrittori. Chiamare navigate non cambia la pagina: propone una nuova location, il router la confronta con quella corrente, aggiorna la cronologia e notifica i componenti che dipendono da quel contesto. Il render arriva dopo. Per questo una chiamata durante il render è un errore: stai chiedendo al router di notificare un componente mentre quel componente si sta ancora calcolando. React lo segnala come aggiornamento di un altro componente durante il render, e in Strict Mode il doppio render raddoppia la richiesta.

## Un passo diagnostico: guarda la location, non lo schermo

Quando «non naviga», la prima domanda è se la location è cambiata. Il frammento qui sotto si sottoscrive al router e stampa ogni transizione: se la path cambia e lo schermo no, il problema è nel rendering della rotta; se la path non cambia, la chiamata non è mai partita o è stata annullata. Ricordati di annullare la sottoscrizione, altrimenti in sviluppo ne accumuli una per ogni hot reload.

// la navigazione è uno stato: guarda cosa cambia davvero const unsubscribe = router.subscribe((state) => { console.log(state.navigation.state, state.location.pathname, state.location.key); }); // chiama unsubscribe() quando smonti il modulo

## Dentro un componente

import { useNavigate } from "react-router"; export function SaveButton() { const navigate = useNavigate(); async function onSave() { await save(); navigate("/dashboard", { replace: true }); } return Salva; }

replace: true sostituisce la voce nella cronologia: dopo un salvataggio il tasto Indietro non deve riportare al form compilato. È una scelta di prodotto, non un dettaglio tecnico.

## Nei loader e nelle action

import { redirect } from "react-router"; export async function loader({ request }: { request: Request }) { const user = await getUser(request); if (!user) throw redirect("/login"); return { user }; }

redirect è un valore di ritorno, non un effetto: interrompe il flusso e il router gestisce la transizione. È la forma corretta quando la decisione dipende da dati caricati prima del render.

## Fuori da React

// router.ts export const router = createBrowserRouter(routes); // http.ts import { router } from "./router"; http.interceptors.response.use(undefined, (error) => { if (error.response?.status === 401) router.navigate("/login"); return Promise.reject(error); });

Gli hook non esistono fuori da un componente: useNavigate in un modulo di rete lancia un errore. L'istanza del router è l'unico punto stabile.

## Quando la correzione non funziona

Se la navigazione avviene ma la pagina non cambia, il problema è il componente che resta montato: due rotte che renderizzano lo stesso elemento con la stessa key. Se invece ottieni un loop, stai chiamando navigate in un effetto senza dipendenze stabili: la nuova location cambia le dipendenze e l'effetto riparte. In quel caso la navigazione appartiene al gestore dell'evento, non all'effetto.

## Quando la correzione comune peggiora le cose

La prima correzione che si incontra è replace: true, utile dopo un salvataggio e dannosa se diventa un'abitudine. Sostituire la voce di cronologia significa che il tasto Indietro salta la schermata precedente: chi voleva tornare al form finisce due passi più indietro, e il passo intermedio non esiste più per nessuno. Anche la misurazione ne risente, perché una pagina che non entra nella cronologia non produce un pageview separato e le funnel sembrano più corte di quanto siano. replace è una decisione di prodotto da prendere caso per caso, non un modo per togliere il form compilato dalla cronologia.

## Un limite con un nome: nessuna istanza nel router dichiarativo

Il limite ha un nome tecnico: router dichiarativo. Con <BrowserRouter> la cronologia vive dentro il componente, quindi un interceptor HTTP o un client generato non hanno nulla da chiamare, e l'istanza citata sopra non esiste. La soluzione non è un trucco con la History API: porta l'evento nel componente, con uno stato condiviso che l'interceptor aggiorna e un effetto che lo legge. Chi passa a React Router 7 in modalità framework trova l'istanza già pronta e non incontra il problema.

## Come prevenirlo

La cronologia fa parte del comportamento, quindi va testata come il comportamento: dopo il salvataggio, Indietro non deve mostrare il form; dopo un 401, la rotta protetta non deve restare raggiungibile con Indietro. Due asserzioni coprono i due bug che questa pagina descrive.

## Dove continuare

Per costruire le schermate: dashboard con shell, componenti e blocchi con preview. Le guide: struttura App Router e copy-paste o headless; il confronto: alternative a MUI misurate.

## FAQ

### Perché `navigate()` durante il render causa un loop?

La navigazione cambia la location, che rirenderizza il componente, che naviga di nuovo. Va chiamata in un gestore di evento o dentro `useEffect` con dipendenze stabili.

### Come navigo dopo una chiamata API?

Nel componente, attendi la promise e poi chiama `navigate`. Se la chiamata è in un layer condiviso, fai restituire il risultato e naviga nel componente.

### `redirect` e `navigate` sono equivalenti?

No. `redirect` viene lanciato in un loader o in un'action e interrompe il flusso; `navigate` è un comando imperativo dal client.

### Funziona con React Router 7 in modalità framework?

Sì, con la stessa distinzione: `useNavigate` nei componenti, `redirect` nei loader. In modalità framework i loader sono la strada preferita.

### Come navigo da un interceptor axios?

Esporta l'istanza del router creata con `createBrowserRouter` e chiama `router.navigate('/login')`. Non usare hook fuori da un componente.
