Sitemap Generation

If you need a list of all routes you have defined (e.g. for statically generating all pages), Dioxus Router provides functions to extract that information from a Segment.

Preparing an app

We will start by preparing an app with some routes like we normally would.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::{history::MemoryHistory, prelude::*};
extern crate dioxus_ssr;

fn Home(cx: Scope) -> Element {
    render! {
        h1 { "Home" }
    }
}

fn Fixed(cx: Scope) -> Element {
    render! {
        h1 { "Fixed" }
        Outlet { }
    }
}

fn Nested(cx: Scope) -> Element {
    render! {
        h2 { "Nested" }
    }
}

struct ParameterName;
fn Parameter(cx: Scope) -> Element {
    let route = use_route(cx).unwrap();
    let param = route.parameter::<ParameterName>().unwrap_or_default();

    render! {
        h1 { "Parameter: {param}" }
    }
}

fn App(cx: Scope) -> Element {
    use_router(
        cx,
        &|| RouterConfiguration {
            synchronous: true,
            history: Box::new(MemoryHistory::with_initial_path("/fixed/nested").unwrap()),
            ..Default::default()
        },
        &|| {
            Segment::content(comp(Home))
                .fixed(
                    "fixed",
                    Route::content(comp(Fixed)).nested(
                        Segment::empty().fixed("nested", comp(Nested))
                    )
                )
                .catch_all((comp(Parameter), ParameterName { }))
        }
    );

    render! {
        Outlet { }
    }
}

let mut vdom = VirtualDom::new(App);
vdom.rebuild();
assert_eq!(dioxus_ssr::render(&mut vdom), "<h1>Fixed</h1><h2>Nested</h2>");
}

Modifying the app to make using sitemaps easier

Preparing our app for sitemap generation is quite easy. We just need to extract our segment definition into its own function.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;
extern crate dioxus_ssr;
fn Home(cx: Scope) -> Element { unimplemented!() }
fn Fixed(cx: Scope) -> Element { unimplemented!() }
fn Nested(cx: Scope) -> Element { unimplemented!() }
struct ParameterName;
fn Parameter(cx: Scope) -> Element { unimplemented!() }

fn App(cx: Scope) -> Element {
    use_router(
        cx,
        &|| RouterConfiguration {
            ..Default::default()
        },
        &prepare_routes
    );

    render! {
        Outlet { }
    }
}

fn prepare_routes() -> Segment<Component> {
    Segment::content(comp(Home))
        .fixed(
            "fixed",
            Route::content(comp(Fixed)).nested(
                Segment::empty().fixed("nested", comp(Nested))
            )
        )
        .catch_all((comp(Parameter), ParameterName { }))
}
}

Sitemaps with parameter names

The first variant to generate sitemaps is very simple. It finds all routes within the Segment and adds them to the returned Vec.

Matching and parameter routes are represented by their key, prefixed with \. Besides that \, all paths are URL encoded.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;
extern crate dioxus_ssr;
fn Home(cx: Scope) -> Element { unimplemented!() }
fn Fixed(cx: Scope) -> Element { unimplemented!() }
fn Nested(cx: Scope) -> Element { unimplemented!() }
struct ParameterName;
fn Parameter(cx: Scope) -> Element { unimplemented!() }
fn prepare_routes() -> Segment<Component> {
    Segment::content(comp(Home))
        .fixed(
            "fixed",
            Route::content(comp(Fixed)).nested(
                Segment::empty().fixed("nested", comp(Nested))
            )
        )
        .catch_all((comp(Parameter), ParameterName { }))
}

let expected = vec![
    "/",
    "/fixed",
    "/fixed/nested",
    // Usually, here would be a fourth result representing the parameter route.
    // However, due to mdbook the name for this file would constantly change,
    // which is why we cannot show it. It would look something like this:
    // "/\\your_crate::ParameterName",
];
let mut sitemap = prepare_routes().gen_sitemap();
sitemap.remove(3); // see above
assert_eq!(sitemap, expected);
}

Sitemaps with actual parameter values

The second variant to generate sitemaps is a bit more involved. When it encounters a parameter route, it inserts all values with a matching key that were provided to it.

Matching routes only add their path if the value matches their regex.

All paths are URL encoded.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
use std::collections::{BTreeMap, HashSet};
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;
extern crate dioxus_ssr;
fn Home(cx: Scope) -> Element { unimplemented!() }
fn Fixed(cx: Scope) -> Element { unimplemented!() }
fn Nested(cx: Scope) -> Element { unimplemented!() }
struct ParameterName;
fn Parameter(cx: Scope) -> Element { unimplemented!() }
fn prepare_routes() -> Segment<Component> {
    Segment::content(comp(Home))
        .fixed(
            "fixed",
            Route::content(comp(Fixed)).nested(
                Segment::empty().fixed("nested", comp(Nested))
            )
        )
        .catch_all((comp(Parameter), ParameterName { }))
}

let parameters = {
    let mut parameters = BTreeMap::new();

    parameters.insert(
        Name::of::<ParameterName>(),
        vec![
            String::from("some-parameter-value"),
            String::from("other-parameter-value")
        ]
    );

    parameters
};

let expected: Vec<String> = vec![
    "/",
    "/fixed",
    "/fixed/nested",
    "/some-parameter-value",
    "/other-parameter-value",
].into_iter().map(String::from).collect();
assert_eq!(expected, prepare_routes().gen_parameter_sitemap(&parameters));
}