search

Defining Routes

When creating a [ Routable] enum, we can define routes for our application using the route("path") attribute.

Route Segments

Each route is made up of segments. Most segments are separated by / characters in the path.

There are four fundamental types of segments:

  1. Static segments are fixed strings that must be present in the path.
  2. Dynamic segments are types that can be parsed from a segment.
  3. Catch-all segments are types that can be parsed from multiple segments.
  4. Query segments are types that can be parsed from the query string.

Routes are matched:

  • First, from most specific to least specific (Static then Dynamic then Catch All) (Query is always matched)
  • Then, if multiple routes match the same path, the order in which they are defined in the enum is followed.

Static segments

Fixed routes match a specific path. For example, the route #[route("/about")] will match the path /about.

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    // Routes always start with a slash
    #[route("/")]
    Home {},
    // You can have multiple segments in a route
    #[route("/hello/world")]
    HelloWorld {},
}

#[component]
fn Home() -> Element {
    todo!()
}

#[component]
fn HelloWorld() -> Element {
    todo!()
}

Dynamic Segments

Dynamic segments are in the form of :name where name is the name of the field in the route variant. If the segment is parsed successfully then the route matches, otherwise the matching continues.

The segment can be of any type that implements FromStr.

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    // segments that start with : are dynamic segments
    #[route("/post/:name")]
    BlogPost {
        // You must include dynamic segments in child variants
        name: String,
    },
    #[route("/document/:id")]
    Document {
        // You can use any type that implements FromStr
        // If the segment can't be parsed, the route will not match
        id: usize,
    },
}

// Components must contain the same dynamic segments as their corresponding variant
#[component]
fn BlogPost(name: String) -> Element {
    todo!()
}

#[component]
fn Document(id: usize) -> Element {
    todo!()
}

Catch All Segments

Catch All segments are in the form of :..name where name is the name of the field in the route variant. If the segments are parsed successfully then the route matches, otherwise the matching continues.

The segment can be of any type that implements FromSegments. (Vec implements this by default)

Catch All segments must be the last route segment in the path (query segments are not counted) and cannot be included in nests.

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    // segments that start with :.. are catch all segments
    #[route("/blog/:..segments")]
    BlogPost {
        // You must include catch all segment in child variants
        segments: Vec<String>,
    },
}

// Components must contain the same catch all segments as their corresponding variant
#[component]
fn BlogPost(segments: Vec<String>) -> Element {
    todo!()
}

Query Segments

Query segments are in the form of ?:name&:othername where name and othername are the names of fields in the route variant.

Unlike Dynamic Segments and Catch All Segments, parsing a Query segment must not fail.

The segment can be of any type that implements FromQueryArgument.

Query segments must be the after all route segments and cannot be included in nests.

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    // segments that start with ?: are query segments
    #[route("/blog?:name&:surname")]
    BlogPost {
        // You must include query segments in child variants
        name: String,
        surname: String,
    },
}

#[component]
fn BlogPost(name: String, surname: String) -> Element {
    rsx! {
        div { "This is your blogpost with a query segment:" }
        div { "Name: {name}" }
        div { "Surname: {surname}" }
    }
}

fn App() -> Element {
    rsx! { Router::<Route> {} }
}

fn main() {}