Defining Routes
When creating a Routable
]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:
- Static segments are fixed strings that must be present in the path.
- Dynamic segments are types that can be parsed from a segment.
- Catch-all segments are types that can be parsed from multiple segments.
- 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(cx: Scope) -> Element { todo!() } #[component] fn HelloWorld(cx: Scope) -> Element { todo!() }
Dynamic Segments
Dynamic segments are in the form of :name
where name
is
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(cx: Scope, name: String) -> Element { todo!() } #[component] fn Document(cx: Scope, 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
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(cx: Scope, segments: Vec<String>) -> Element { todo!() }
Query Segments
Query segments are in the form of ?:name
where name
is the name of the field 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 FromQuery
.
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?:query_params")] BlogPost { // You must include query segments in child variants query_params: BlogQuerySegments, }, } #[derive(Debug, Clone, PartialEq)] struct BlogQuerySegments { name: String, surname: String, } /// The display impl needs to display the query in a way that can be parsed: impl Display for BlogQuerySegments { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "name={}&surname={}", self.name, self.surname) } } /// The query segment is anything that implements https://docs.rs/dioxus-router/latest/dioxus_router/routable/trait.FromQuery.html. You can implement that trait for a struct if you want to parse multiple query parameters. impl FromQuery for BlogQuerySegments { fn from_query(query: &str) -> Self { let mut name = None; let mut surname = None; let pairs = form_urlencoded::parse(query.as_bytes()); pairs.for_each(|(key, value)| { if key == "name" { name = Some(value.clone().into()); } if key == "surname" { surname = Some(value.clone().into()); } }); Self { name: name.unwrap(), surname: surname.unwrap(), } } } #[component] fn BlogPost(cx: Scope, query_params: BlogQuerySegments) -> Element { render! { div{"This is your blogpost with a query segment:"} div{format!("{:?}", query_params)} } } fn App(cx: Scope) -> Element { render! { Router::<Route>{} } } fn main() {}