Repository navigation
Routing Navigation
The blinc_router crate provides cross-platform routing with path matching, navigation history, guards, page transitions, and deep linking.
use blinc_router::{RouterBuilder, Route, PageTransition};
let router = RouterBuilder::new()
.route(Route::new("/").name("home").view(home_page))
.route(Route::new("/users").name("users").view(users_page)
.child(Route::new("/:id").name("user").view(user_detail)))
.route(Route::new("/settings")
.view(settings_page)
.transition(PageTransition::modal()))
.not_found(not_found_page)
.build();// Push (adds to history)
router.push("/users/42");
// Named route with params
router.push_named("user", &[("id", "42")]);
// Replace (no history entry)
router.replace("/login");
// Back / Forward
router.back();
router.forward();
// Check state
router.can_go_back();
router.current_path();
router.params().get("id");
router.query().get("page");Place router.outlet() where the page content should render:
fn build_ui(ctx: &WindowedContext) -> impl ElementBuilder {
div().flex_col()
.child(nav_bar(&router))
.child(router.outlet()) // Current route renders here
}On navigation the outlet swaps its view in place. The scaffold around it (header, nav bar) is left alone, and each navigation builds the view again, even to the same path.
Inside route views, use_router() returns the active router:
fn user_detail(ctx: RouteContext) -> Div {
let router = use_router(); // Same as ctx.router
let id = ctx.params.get("id").unwrap_or("?");
div()
.child(text(&format!("User #{}", id)))
.child(
div().on_click(move |_| router.back())
.child(text("Back"))
)
}Nested routers work automatically — use_router() returns whichever router's outlet() is currently building.
Per-route transitions using Blinc's animation system:
Route::new("/settings")
.view(settings_page)
.transition(PageTransition::slide()) // iOS push style
.transition(PageTransition::fade()) // Crossfade
.transition(PageTransition::modal()) // Slide up/down
.transition(PageTransition::scale()) // Scale in/out
.transition(PageTransition::none()) // Instant
// Custom with spring physics
.transition(PageTransition::slide().with_spring(SpringConfig::bouncy()))A route with a transition plays the new page's enter animation and keeps the old page mounted, out of flow at the start of the outlet, until its exit animation is done.
Protect routes with guards that allow, redirect, or reject:
use blinc_router::{NavigationGuard, GuardResult};
use std::sync::Arc;
let auth_guard: NavigationGuard = Arc::new(|_from, _to| {
if is_authenticated() {
GuardResult::Allow
} else {
GuardResult::Redirect("/login".into())
}
});
RouterBuilder::new()
.route(Route::new("/dashboard").view(dashboard).guard(auth_guard))
.build();Deep linking is automatic — just build a router and it works on all platforms.
RouterBuilder::build() auto-registers the deep link handler and back button.
// That's it — no platform-specific setup needed in Rust
let router = RouterBuilder::new()
.route(Route::new("/users/:id").view(user_page))
.build();
// Deep links to myapp://host/users/42 automatically navigateAndroid — add intent filters in AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="myapp" />
</intent-filter>iOS — add URL types in Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>Desktop — register a custom URL scheme with the OS:
macOS (Info.plist):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>Windows (registry, set up by installer):
HKEY_CLASSES_ROOT\myapp\shell\open\command = "C:\path\to\myapp.exe" "--deep-link=%1"
Linux (.desktop file):
MimeType=x-scheme-handler/myapp
Exec=myapp --deep-link=%uCLI fallback:
myapp --deep-link=myapp://host/users/42-
RouterBuilder::build()registers a global deep link handler - Platform runners auto-dispatch incoming URIs to the handler
- The router parses the URI and calls
push(path) - No user code needed beyond building the router
Also automatic — RouterBuilder::build() registers a back button handler.
- Android: system back button navigates back if the router has history
-
Desktop:
Key::Backdispatches through the back handler stack - If at the root route, the event propagates (app exits normally)
Express-style path patterns:
| Pattern | Example | Matches |
|---|---|---|
| Static | /about |
Exact match |
| Parameter | /users/:id |
/users/42 → {id: "42"}
|
| Wildcard | /files/*path |
/files/a/b/c → {path: "a/b/c"}
|
| Nested | parent + child |
/users + /:id → /users/42
|
| Query | any path |
/search?q=hello → {q: "hello"}
|
Look up routes by name for type-safe navigation:
// Check if a named route exists
router.path_for("user"); // Some("/users/:id")
// Navigate with params
router.push_named("user", &[("id", "42")]); // → /users/42
// Check if a path matches
router.has_route("/users/42"); // trueUse the tabs() component from blinc_cn with the router's current path as the active tab:
use blinc_cn::tabs;
fn app_shell(router: &Router) -> Div {
// Track active tab via router path
let active_tab = ctx.use_state_keyed("tab", || router.current_path());
div().flex_col().w_full().h_full()
// Content area — router outlet
.child(router.outlet().flex_grow())
// Bottom tab bar
.child(
tabs(&active_tab)
.tab("Home", "/", {
let r = router.clone();
move || r.push("/")
})
.tab("Search", "/search", {
let r = router.clone();
move || r.push("/search")
})
.tab("Profile", "/profile", {
let r = router.clone();
move || r.push("/profile")
})
)
}Each tab click calls router.push() which updates the outlet. The tab state stays in sync with the route.
The router maintains a page stack — pages persist in the tree when new pages are pushed on top. Suspended pages have input disabled and are hidden, but their state (scroll position, form values, etc.) is preserved.
// Renders the page stack — active page visible, suspended pages preserved
router.outlet()When you router.push("/details"):
- The current page becomes Suspended (opacity 0, pointer_events_none)
- The new page is pushed as Active on top
When you router.back():
- The top page is removed from the stack
- The page below becomes Active again (with preserved state)
use blinc_router::PageState;
let pages = router.page_stack();
for page in &pages {
match page.state {
PageState::Active => println!("Visible: {}", page.route.path),
PageState::Suspended => println!("Hidden: {}", page.route.path),
}
}Use motion() containers inside route views for animated transitions:
fn user_detail(ctx: RouteContext) -> Div {
motion()
.slide_in(SlideDirection::Right, 300)
.child(
div().w_full().h_full()
.child(text(&format!("User #{}", ctx.params.get("id").unwrap_or("?"))))
.child(
div().on_click({
let r = ctx.router.clone();
move |_| r.back()
})
.child(text("Back"))
)
)
}Layout routes can contain their own scoped router for sub-navigation.
use_router() automatically returns the innermost router:
fn dashboard_layout(ctx: RouteContext) -> Div {
// Create a sub-router for dashboard tabs
let sub_router = RouterBuilder::new()
.route(Route::new("/").view(dashboard_overview))
.route(Route::new("/analytics").view(analytics))
.route(Route::new("/settings").view(settings))
.initial(&ctx.path) // Start at current sub-path
.build();
div().flex_row().w_full().h_full()
.child(dashboard_sidebar(&sub_router))
.child(sub_router.outlet()) // Nested outlet — use_router() returns sub_router here
}Use sheet() from blinc_cn for modal-like navigation that slides up from the bottom:
use blinc_cn::sheet;
fn show_details(router: &Router, item_id: &str) {
// Navigate to detail route
router.push(&format!("/items/{}", item_id));
// Or show as a bottom sheet overlay
sheet()
.title("Item Details")
.content(move || {
let router = use_router();
router.outlet() // Render the matched route inside the sheet
})
.show();
}For gesture-dismissable sheets on mobile, the sheet component handles the swipe-down gesture automatically. On dismiss, call router.back():
sheet()
.on_close({
let r = router.clone();
move || r.back()
})
.content(|| detail_view())
.show();| Pattern | Widget | Router Integration |
|---|---|---|
| Page navigation | router.outlet() |
Direct — renders current route |
| Tab bar | blinc_cn::tabs() |
Tab clicks call router.push()
|
| Stack with animations |
stack() + motion()
|
Wrap route views in motion containers |
| Bottom sheet | blinc_cn::sheet() |
Content renders router.outlet(), dismiss calls router.back()
|
| Drawer / sidebar | blinc_cn::drawer() |
Navigation links call router.push()
|
| Back button | Auto-registered |
RouterBuilder::build() wires it |
| Deep links | Auto-registered | Platform dispatches to router automatically |
Getting Started
Mobile Development
Example Gallery
- Example Gallery
- Canvas Element
- Canvas Kit Interactive
- Carousel Demo - Selector API Showcase
- Chrome-Style Tabs
- blinc_cn Components
- Code Element
- Complex SVG
- CSS Debug
- CSS Visual Features
- Layer Effects
- Emoji and HTML Entities
- @flow Shader
- Fluid Surface
- Skeleton animation with glTF +
blinc_canvas_kit. DrawContext::run_gpu_passend-to-end demo.- Image CSS Styling
- Image Layer Test
- Keyframe Animation Canvas
- Markdown Editor
- 3D Mesh Demo — renders the Khronos glTF
DamagedHelmetsample model - Motion Demo
- Music Player Glass Card
- Node-editor demo — pre-wired graph with three node types, typed
- Notch Menu Bar
- Overflow Fade
- Overlay System
- Pointer Query
- Rich Text Element
- Rich Text Editor
- Scroll Container
- Semantic @flow
- Sortable
- Stateful API + Signal-bound modifiers demo.
- End-to-end 3D demo wiring Blinc's SceneKit3D renderer up to
- Unified Styling API
- SVG Animation
- Table Builder
- Tabler Icons
- Minimal text positioning test
- Text Input Widgets
- KHR_texture_transform
- Theme System
- Timeline Animation
- Typography
- Video Player
- Wet Glass
- Windowed Application
Web Development
Core Concepts
- Elements & Layout
- Styling & Materials
- CSS Styling
- Theming
- Event Handling
- State Management
- Lists & Conditional Content
- Element Refs
Animation
Components
Component Library (blinc_cn)
Widgets
- Buttons & Inputs
- Text & Rich Text
- Code Editor
- Scroll Containers
- Virtualized List
- Canvas Drawing
- Images & SVG
- Audio & Video
- Markdown Rendering
Canvas Kit
Advanced
- Routing & Navigation
- Multi-Window
- System Integration
- Element Query API
- Overlay System
- Custom State Machines
- Pointer Query
- Performance Tips
- Flow Shaders
- 3D Rendering
- Custom GPU Passes
- Hot-reload (experimental)
Architecture
Contributing