DioxusLabs/dioxus · error

A router must be present to use navigator

Error message

A router must be present to use navigator

What it means

navigator() acquires the RouterContext that a Router component provides to its subtree. Without a Router ancestor the context doesn't exist, try_consume_context returns None, and the expect panics. Unlike a hook, navigator() may be called from anywhere inside RSX or event handlers, but still only under a mounted Router.

Source

Thrown at packages/router/src/contexts/navigator.rs:13

use crate::{ExternalNavigationFailure, NavigationTarget, RouterContext};

/// Acquire the navigator without subscribing to updates.
///
/// Can be called anywhere in the application provided a Router has been initialized.
///
/// ## Panics
///
/// Panics if there is no router present.
pub fn navigator() -> Navigator {
    Navigator(
        dioxus_core::try_consume_context::<RouterContext>()
            .expect("A router must be present to use navigator"),
    )
}

/// A view into the navigation state of a router.
#[derive(Clone, Copy)]
pub struct Navigator(pub(crate) RouterContext);

impl Navigator {
    /// Check whether there is a previous page to navigate back to.
    #[must_use]
    pub fn can_go_back(&self) -> bool {
        self.0.can_go_back()
    }

    /// Check whether there is a future page to navigate forward to.
    #[must_use]
    pub fn can_go_forward(&self) -> bool {
        self.0.can_go_forward()

View on GitHub (pinned to 393d190a80)

Solutions

  1. Wrap your app in a Router: define #[derive(Routable)] enum Route and launch the router (Router::<Route> {})
  2. If a component must work with or without a router, gate the call: check try_consume_context::<RouterContext>() first
  3. For optional navigation, fall back to staying on the current view when the context is absent

Example fix

// before
rsx! { button { onclick: move |_| navigator().push("/settings"), "Go" } } // no Router mounted
// after
#[derive(Clone, Routable)]
enum Route { #[route("/settings")] Settings {}, #[route("/")] Home {} }
// root component:
rsx! { Router::<Route> {} }
Defensive patterns

Strategy: type-guard

Validate before calling

if dioxus::prelude::try_consume_context::<dioxus::router::RouterContext>().is_some() {
    let nav = dioxus::router::navigator();
    nav.push("/settings");
} else {
    // no router mounted: fall back (inert link, no-op, or render Router first)
}

Type guard

fn router_present() -> bool {
    dioxus::prelude::try_consume_context::<dioxus::router::RouterContext>().is_some()
}

Prevention

When it happens

Trigger: Calling navigator() in a tree that never mounts a Router (plain dioxus::launch(Component) app, LaunchBuilder without a Routable root), or invoking it in a context outside the component tree (background tasks, server code).

Common situations: Adding navigator().push(...) navigation to a small demo or test app that has no router set up; refactoring a component out of a routed app into a library; calling navigator in code paths that also run on the server.

Related errors


AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16). Data as JSON: /api/errors/8f495545b085f504. Report an issue: GitHub.