Nested Routes
When developing bigger applications we often want to nest routes within each other. As an example, we might want to organize a settings menu using this pattern:
└ Settings
├ General Settings (displayed when opening the settings)
├ Change Password
└ Privacy Settings
We might want to map this structure to these paths and components:
/settings -> Settings { GeneralSettings }
/settings/password -> Settings { PWSettings }
/settings/privacy -> Settings { PrivacySettings }
Nested routes allow us to do this.
Route Depth
With nesting routes, the router manages content on multiple levels. In our
example, when the path is /settings, there are two levels of content:
- The
Settingscomponent - The
GeneralSettingscomponent
Dioxus Router uses the Outlet component to actually render content, but each
Outlet can only render content from one level. This means that for the
content of nested routes to actually be rendered, we also need nested
Outlets.
Defining the content components
We start by creating the components we want the router to render.
Take a look at the Settings component. When it gets rendered by an Outlet,
it will render a second Outlet. Thus the second Outlet is nested within
the first one, and will in turn render our nested content.
#![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 Settings(cx: Scope) -> Element { render! { h1 { "Settings" } Outlet { } } } fn GeneralSettings(cx: Scope) -> Element { render! { h2 { "General Settings" } } } fn PWSettings(cx: Scope) -> Element { render! { h2 { "Password Settings" } } } fn PrivacySettings(cx: Scope) -> Element { render! { h2 { "Privacy Settings" } } } }
Defining the root Segment
Now we create the Segment that we will pass to the router.
Note that we wrap comp(Settings) within a Route. For this exact code that
is unnecessary, as this would be done automatically. However, in the next step
we'll use a method of Route, so we might as well add this now.
#![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 Settings(cx: Scope) -> Element { unimplemented!() } fn App(cx: Scope) -> Element { use_router( cx, &|| RouterConfiguration { ..Default::default() }, &|| Segment::empty().fixed("settings", Route::content(comp(Settings))) ); // ... unimplemented!() } }
Defining the nested Segment
In order to create nested routes we need to create a nested Segment. We then
pass it to the Route on the root segment.
A
Segmentalways refers to one exact segment of the path.https://router.example/
root_segment/first_nested_segment/second_nested_segment/...
#![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 Settings(cx: Scope) -> Element { unimplemented!() } fn GeneralSettings(cx: Scope) -> Element { unimplemented!() } fn PWSettings(cx: Scope) -> Element { unimplemented!() } fn PrivacySettings(cx: Scope) -> Element { unimplemented!() } fn App(cx: Scope) -> Element { use_router( cx, &|| RouterConfiguration { ..Default::default() }, &|| Segment::empty().fixed( "settings", Route::content(comp(Settings)).nested( Segment::content(comp(GeneralSettings)) .fixed("password", comp(PWSettings)) .fixed("privacy", comp(PrivacySettings)) ) ) ); // ... unimplemented!() } }
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; fn Settings(cx: Scope) -> Element { render! { h1 { "Settings" } Outlet { } } } fn GeneralSettings(cx: Scope) -> Element { render! { h2 { "General Settings" } } } fn PWSettings(cx: Scope) -> Element { render! { h2 { "Password Settings" } } } fn PrivacySettings(cx: Scope) -> Element { render! { h2 { "Privacy Settings" } } } fn App(cx: Scope) -> Element { use_router( cx, &|| RouterConfiguration { synchronous: true, history: Box::new(MemoryHistory::with_initial_path("/settings/privacy").unwrap()), ..Default::default() }, &|| Segment::empty().fixed( "settings", Route::content(comp(Settings)).nested( Segment::content(comp(GeneralSettings)) .fixed("password", comp(PWSettings)) .fixed("privacy", comp(PrivacySettings)) ) ) ); render! { Outlet { } } } let mut vdom = VirtualDom::new(App); vdom.rebuild(); assert_eq!( dioxus_ssr::render(&vdom), "<h1>Settings</h1><h2>Privacy Settings</h2>" ); }