9.3 KiB
Dioxus Router Architecture
The Dioxus router provides type-safe, declarative routing with support for nested layouts, dynamic parameters, and platform-agnostic navigation.
Route Definition
Routable Trait
Routes are defined using Rust enums with the #[derive(Routable)] macro:
#[derive(Routable, Clone, PartialEq, Debug)]
enum Route {
#[route("/")]
Home {},
#[route("/blog/:blog_id")]
Blog { blog_id: usize },
#[route("/edit?:blog_id")]
Edit { blog_id: usize },
#[route("/hashtag/#:hash")]
Hash { hash: String },
}
The Routable trait provides:
const SITE_MAP: Static map of all route segmentsrender(level: usize) -> Element: Renders component at nesting levelFromStrimplementation: Parses URLs into route enumsDisplayimplementation: Converts routes to URLs- Helper methods:
is_child_of(),parent(),static_routes()
Route Matching Priority
- Query routes (
/?:query) - Static routes (
/route) - Dynamic routes (
/:route) - Catch-all routes (
/:..route)
Segment Types
- Static: Literal path segments like
/blog - Dynamic: Single parameters like
/:id(implementsFromRouteSegment) - CatchAll: Multiple segments like
/:..segments(implementsFromRouteSegments)
Query Parameters
- Spread query:
/?:..query(entire query string, usesFromQuery) - Segmented query:
/?:param1&:param2(individual params, useFromQueryArgument)
Hash Fragments
/#:hash_segment (uses FromHashFragment)
Navigation
RouterContext
Manages navigation state with these core methods:
impl RouterContext {
// Navigation
pub fn push(&self, target: impl Into<NavigationTarget>);
pub fn replace(&self, target: impl Into<NavigationTarget>);
// History
pub fn go_back(&self);
pub fn go_forward(&self);
pub fn can_go_back(&self) -> bool;
pub fn can_go_forward(&self) -> bool;
// Introspection
pub fn current<R: Routable>(&self) -> R;
pub fn full_route_string(&self) -> String;
pub fn prefix(&self) -> Option<String>;
// Site map
pub fn site_map(&self) -> &'static [SiteMapSegment];
}
The router context is:
- Created in
Routercomponent withRouterConfig - Provided via Dioxus context to children
- Uses signals for reactive updates
- Tracks subscribers for re-rendering
- Integrates with history provider
NavigationTarget
pub enum NavigationTarget<R = String> {
Internal(R), // Route parsed by router
External(String), // External URL (browser handles)
}
Link Component
Declarative navigation:
Link {
to: NavigationTarget,
active_class: Option<String>, // Applied when matches current
class: Option<String>,
new_tab: bool,
onclick: Option<EventHandler>,
children: Element,
}
Behavior:
- Accesses router without subscribing
- Checks if target matches current for
active_class - On click (left button, no modifiers):
- Prevents default
- Routes via
router.push_any(target) - Executes optional onclick
- Generates
<a>tag withhreffor SEO - External links use default browser navigation
Nested Routes
Layouts and Outlets
#[derive(Routable)]
enum Route {
#[nest("/admin")]
#[layout(AdminLayout)]
#[route("/")]
AdminHome {},
#[route("/users")]
Users {},
#[end_layout]
#[end_nest]
#[route("/")]
Home {},
}
#[component]
fn AdminLayout() -> Element {
rsx! {
header { "Admin Panel" }
Outlet::<Route> {} // Children render here
}
}
Outlet Component
- Renders child routes at correct nesting depth
- Uses
OutletContextto track depth level - Works with layout wrapping via macro
VirtualDOM Integration
Routable::render(level)matches route at nesting level- Each layout increments nesting level
- Router changes mark context-accessing components dirty
- Outlet at correct depth renders matched component
Route Parameters
Parameter Traits
| Component | Trait | Example |
|---|---|---|
| Path segment | FromRouteSegment / ToRouteSegment |
/users/:id |
| Multiple segments | FromRouteSegments / ToRouteSegments |
/files/:..path |
| Query full | FromQuery |
/?:q=search |
| Query arg | FromQueryArgument |
/?page=1&sort=name |
| Hash | FromHashFragment |
/#section-id |
Auto-Implementation
Traits auto-implement for types with standard traits:
FromRouteSegment:FromStr + DefaultFromQueryArgument:FromStr + DefaultFromQuery:From<&str>FromHashFragment:FromStr + Default
URL Encoding
- Path segments:
PATH_ASCII_SET(excludes/,?,#) - Query strings:
QUERY_ASCII_SET(excludes&,=) - Hash fragments:
FRAGMENT_ASCII_SET - Auto encode/decode during parse/display
Router Macro
Code Generation
#[derive(Routable)] generates three implementations:
1. FromStr Implementation:
impl std::str::FromStr for Route {
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Split URL into path, query, hash
// Percent-decode segments
// Try matching against all routes (specificity order)
// Return error with attempted routes if no match
}
}
2. Display Implementation:
impl std::fmt::Display for Route {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
Route::Blog { blog_id } => {
write!(f, "/blog/{}", blog_id)?;
// Percent-encode as needed
}
}
}
}
3. Routable Trait Implementation:
impl Routable for Route {
const SITE_MAP: &'static [SiteMapSegment] = &[...];
fn render(&self, level: usize) -> Element {
match (level, self) {
(0, Self::Blog { .. }) => rsx! { Layout { Outlet {} } }
(1, Self::Blog { blog_id }) => rsx! { Blog { blog_id: *blog_id } }
_ => VNode::empty()
}
}
}
Error Type Generation
Each route generates detailed error enum:
pub enum RouteMatchError {
Home(HomeParseError),
Blog(BlogParseError),
BlogIdParseError(ParseIntError),
}
Segment Parsing
pub enum RouteSegment {
Static(String), // "/blog" -> Static("blog")
Dynamic(Ident, Type), // "/:id" -> Dynamic(id, u32)
CatchAll(Ident, Type), // "/:..paths" -> CatchAll(paths, Vec<String>)
}
Process:
- Split URL into path, query, hash
- Remove trailing slashes
- Percent-decode components
- Split path by
/ - Classify segments (static/dynamic/catch-all)
- Match field names to route parameters
- Validate types exist in struct fields
History Abstraction
History Trait
Platform abstraction for navigation:
pub trait History {
fn current_route(&self) -> String;
fn current_prefix(&self) -> Option<String>;
fn can_go_back(&self) -> bool;
fn can_go_forward(&self) -> bool;
fn go_back(&self);
fn go_forward(&self);
fn push(&self, route: String);
fn replace(&self, path: String);
fn external(&self, url: String) -> bool;
fn updater(&self, callback: Arc<dyn Fn() + Send + Sync>);
fn include_prevent_default(&self) -> bool;
}
MemoryHistory
In-memory fallback implementation:
pub struct MemoryHistory {
state: RefCell<MemoryHistoryState>,
base_path: Option<String>,
}
struct MemoryHistoryState {
current: String,
history: Vec<String>, // For back
future: Vec<String>, // For forward
}
Behavior:
- Default starts at
/ push: Adds current to history, sets new as current, clears futurereplace: Only changes currentgo_back/forward: Swaps between stacks- Does not navigate external URLs
Use Cases:
- Testing/SSR without browser history
- In-memory fullstack navigation
- Development without browser
Platform Implementations
Web: Browser History API, popstate events Desktop: Platform-specific window/navigation APIs LiveView: WebSocket with server-managed state
VirtualDOM Integration
-
Initialization:
RoutercreatesRouterContextwith config- Provides to descendants via context
- Gets current route from history
- Syncs with history if needed
-
URL Change:
RouterContext::push_any(NavigationTarget)called- History provider's method invoked
change_route()callback fires- Subscribers marked dirty
-
Re-render:
- Components using
use_router()/use_navigator()marked dirty Routable::render(level)generates new tree- Layouts render at nesting levels
Outletrenders final component
- Components using
-
Parameter Passing:
- Route parameters part of enum variant
- Passed as component props
- Type-safe at compile time
Key Design Decisions
- Trait-Based Extensibility: Custom types implement parameter traits
- Type Safety: Routes are enums - invalid routes are compile errors
- Macro-Driven: Reduces boilerplate, efficient matching
- Platform Abstraction: History trait enables different renderers
- Reactive Updates: Signals and subscriptions for efficient re-renders
- Nested Layouts: Outlets allow composition without complexity
- URL Encoding: Automatic percent-encoding/decoding
- SEO-Friendly: Links generate real
<a>tags with hrefs