Catch All Routes
Many modern web apps store parameters within their current path. This allows users to share URLs that link to a specific bit of content. We can create this functionality with catch all routes.
If you want to change what route is active based on the format of the parameter, see Matching Routes.
The parameter will be URL decoded.
Creating a content component
We start by creating a component that uses the parameters value.
We can get the current state of the router using the use_route
hook. From
that state we can extract the current value of our parameter by using a key we
will later also define on our route.
It is VERY IMPORTANT to drop the object returned by the
use_route
hook once our component finished rendering. Otherwise the entire router will be frozen.
The
use_route
hook can only be used in components nested within a component that calleduse_router
.
#![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::*; struct Name; fn Greeting(cx: Scope) -> Element { let route = use_route(cx).expect("is nested within a Router component"); let name = route.parameter::<Name>() .map(|name| name.clone()) .unwrap_or(String::from("world")); render! { p { "Hello, {name}!" } } } }
Defining the routes
Now we can define our route. Unlike a fixed Route
, a ParameterRoute
needs two arguments to be created.
Also note that each
Segment
can have exactly one parameter or fallback route.For that reason, the example below would not work in practice, but showing both forms (explicit and short) is more important for this example.
#![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::*; fn Greeting(cx: Scope) -> Element { unimplemented!() } struct Name; fn App(cx: Scope) -> Element { use_router( cx, &|| RouterConfiguration { ..Default::default() }, &|| { Segment::empty() .catch_all(ParameterRoute::content::<Name>(comp(Greeting))) .catch_all((comp(Greeting), Name { })) // same in short } ); // ... unimplemented!() } }
Interaction with other routes
Each individual Segment
can only ever have one active route. This means that
when a Segment
has more than just a catch all route, the router has to
decide which is active. It does that this way:
- If the segment is not specified (i.e.
/
), then the index route will be active. - If a fixed route matches the current path, it will be active.
- If a matching route matches the current path, it will be active. Matching routes are checked in the order they are defined.
- If neither a fixed nor a matching route is active, the catch all route or fallback route will be.
Step 0 means that if we want a parameter to be empty, that needs to be specified
by the path, i.e. //
.
Be careful with using catch all routes on the root
Segment
. Navigating to paths starting with//
will NOT work. This is not a limitation of the router, but rather of how relative URLs work.If you absolutely need an empty parameter on the root
Segment
, a URL like this could work:
https://your-site.example//
for web sitesdioxus://index.html//
for desktop apps
Full Code
#![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; struct Name; fn Greeting(cx: Scope) -> Element { let route = use_route(cx).expect("is nested within a Router component"); let name = route.parameter::<Name>() .map(|name| name.clone()) .unwrap_or(String::from("world")); render! { p { "Hello, {name}!" } } } fn App(cx: Scope) -> Element { let routes = use_router( cx, &|| RouterConfiguration { synchronous: true, history: Box::new(MemoryHistory::with_initial_path("/Dioxus").unwrap()), ..Default::default() }, &|| Segment::empty().catch_all((comp(Greeting), Name { })) ); // ... render! { Outlet { } } } let mut vdom = VirtualDom::new(App); vdom.rebuild(); assert_eq!( dioxus_ssr::render(&vdom), "<p>Hello, Dioxus!</p>" ); }