Crate leptos_router

source ·
Expand description

Leptos Router

Leptos Router is a router and state management tool for web applications written in Rust using the Leptos web framework. It is ”isomorphic,” i.e., it can be used for client-side applications/single-page apps (SPAs), server-side rendering/multi-page apps (MPAs), or to synchronize state between the two.

Philosophy

Leptos Router is built on a few simple principles:

  1. URL drives state. For web applications, the URL should be the ultimate source of truth for most of your app’s state. (It’s called a Universal Resource Locator for a reason!)

  2. Nested routing. A URL can match multiple routes that exist in a nested tree and are rendered by different components. This means you can navigate between siblings in this tree without re-rendering or triggering any change in the parent routes.

  3. Progressive enhancement. The A and Form components resolve any relative nested routes, render actual <a> and <form> elements, and (when possible) upgrading them to handle those navigations with client-side routing. If you’re using them with server-side rendering (with or without hydration), they just work, whether JS/WASM have loaded or not.

Example

 
use leptos::*;
use leptos_router::*;

#[component]
pub fn RouterExample(cx: Scope) -> impl IntoView {
  view! {
    cx,
    <div id="root">
      // we wrap the whole app in a <Router/> to allow client-side navigation
      // from our nav links below
      <Router>
        // <nav> and <main> will show on every route
        <nav>
          // LR will enhance the active <a> link with the [aria-current] attribute
          // we can use this for styling them with CSS like `[aria-current] { font-weight: bold; }`
          <A href="contacts">"Contacts"</A>
          // But we can also use a normal class attribute like it is a normal component
          <A href="settings" class="my-class">"Settings"</A>
          // It also supports signals!
          <A href="about" class=move || "my-class">"About"</A>
        </nav>
        <main>
          // <Routes/> both defines our routes and shows them on the page
          <Routes>
            // our root route: the contact list is always shown
            <Route
              path=""
              view=move |cx| view! { cx,  <ContactList/> }
            >
              // users like /gbj or /bob
              <Route
                path=":id"
                view=move |cx| view! { cx,  <Contact/> }
              />
              // a fallback if the /:id segment is missing from the URL
              <Route
                path=""
                view=move |_| view! { cx,  <p class="contact">"Select a contact."</p> }
              />
            </Route>
            // LR will automatically use this for /about, not the /:id match above
            <Route
              path="about"
              view=move |cx| view! { cx,  <About/> }
            />
          </Routes>
        </main>
      </Router>
    </div>
  }
}

type ContactSummary = (); // TODO!
type Contact = (); // TODO!()

// contact_data reruns whenever the :id param changes
async fn contact_data(id: String) -> Contact {
  todo!()
}

// contact_list_data *doesn't* rerun when the :id changes,
// because that param is nested lower than the <ContactList/> route
async fn contact_list_data() -> Vec<ContactSummary> {
  todo!()
}

#[component]
fn ContactList(cx: Scope) -> impl IntoView {
  // loads the contact list data once; doesn't reload when nested routes change
  let contacts = create_resource(cx, || (), |_| contact_list_data());
  view! {
    cx,
    <div>
      // show the contacts
      <ul>
        {move || contacts.read(cx).map(|contacts| view! { cx, <li>"todo contact info"</li> } )}
      </ul>

      // insert the nested child route here
      <Outlet/>
    </div>
  }
}

#[component]
fn Contact(cx: Scope) -> impl IntoView {
  let params = use_params_map(cx);
  let data = create_resource(
    cx,
    move || params.with(|p| p.get("id").cloned().unwrap_or_default()),
    move |id| contact_data(id)
  );
  todo!()
}

#[component]
fn About(cx: Scope) -> impl IntoView {
  todo!()
}

Module Route Definitions

Routes can also be modularized and nested by defining them in separate components, which can be located in and imported from other modules. Components that return <Route/> should be marked #[component(transparent)], as in this example:

use leptos::*;
use leptos_router::*;

#[component]
pub fn App(cx: Scope) -> impl IntoView {
  view! { cx,
    <Router>
      <Routes>
        <Route path="/" view=move |cx| {
          view! { cx, "-> /" }
        }/>
        <ExternallyDefinedRoute/>
      </Routes>
    </Router>
  }
}

// `transparent` here marks the component as returning data (a RouteDefinition), not a view
#[component(transparent)]
pub fn ExternallyDefinedRoute(cx: Scope) -> impl IntoView {
  view! { cx,
    <Route path="/some-area" view=move |cx| {
      view! { cx, <div>
        <h2>"Some Area"</h2>
        <Outlet/>
      </div> }
    }>
      <Route path="/path-a/:id" view=move |cx| {
        view! { cx, <p>"Path A"</p> }
      }/>
      <Route path="/path-b/:id" view=move |cx| {
        view! { cx, <p>"Path B"</p> }
      }/>
    </Route>
  }
}

Feature Flags

  • csr Client-side rendering: Generate DOM nodes in the browser
  • ssr Server-side rendering: Generate an HTML string (typically on the server)
  • hydrate Hydration: use this to add interactivity to an SSRed Leptos app
  • stable By default, Leptos requires nightly Rust, which is what allows the ergonomics of calling signals as functions. Enable this feature to support stable Rust.

Important Note: You must enable one of csr, hydrate, or ssr to tell Leptos which mode your app is operating in.

Re-exports

  • pub use matching::*;

Macros

Structs

Enums

  • An error that occurs during navigation.
  • Errors that can occur while parsing params using Params.
  • Indicates which rendering mode should be used for this route during server-side rendering.

Traits

  • Tries to deserialize a type from form data. This can be used for client-side validation during form submission.
  • The Router relies on a RouterIntegrationContext, which tells the router how to find things like the current URL, and how to navigate to a new page. The History trait can be implemented on any type to provide this information.
  • A simple method of deserializing key-value data (like route params or URL search) into a concrete data type. Self should typically be a struct in which each field’s type implements FromStr.
  • Describes a value that is either a static or a reactive URL, i.e., a String, a &str, or a reactive Fn() -> String.

Functions

  • An HTML a progressively enhanced to use client-side routing.
  • Automatically turns a server Action into an HTML form progressively enhanced to use client-side routing.
  • An HTML form progressively enhanced to use client-side routing.
  • Automatically turns a server MultiAction into an HTML form progressively enhanced to use client-side routing.
  • Displays the child route nested in a parent route, allowing you to control exactly where that child route is displayed. Renders nothing if there is no nested child.
  • Redirects the user to a new URL, whether on the client side or on the server side. If rendered on the server, this sets a 302 status code and sets a Location header. If rendered in the browser, it uses client-side navigation to redirect. In either case, it resolves the route relative to the current route. (To use an absolute path, prefix it with /).
  • Describes a portion of the nested layout of the app, specifying the route it should match, the element it should display, and data that should be loaded alongside the route.
  • Provides for client-side and server-side routing. This should usually be somewhere near the root of the application.
  • Contains route definitions and manages the actual routing process.
  • Creates a reactive location from the given path and state.
  • Generates a list of all routes this application could possibly serve. This returns the raw routes in the leptos_router format. Odds are you want generate_route_list() from either the actix, axum, or viz integrations if you want to work with their router
  • Provides a function that can be used to redirect the user to another absolute path, on the server. This should set a 302 status code and an appropriate Location header.
  • Returns the current Location, which contains reactive variables
  • Returns a function that can be used to navigate to a new route.
  • Returns the current route params, parsed into the given type, or an error.
  • Returns a raw key-value map of route params.
  • Returns the current URL search query, parsed into the given type, or an error.
  • Returns a raw key-value map of the URL search query.
  • Resolves the given path relative to the current route.
  • Returns the current RouteContext, containing information about the matched route.
  • Returns the current RouterContext, containing information about the router’s state.