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
Settings
component - The
GeneralSettings
component
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
Outlet
s.
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
Segment
always 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>" ); }