leptosbook
SvelteKit-inspired UI primitives for Leptos — bringing joy and ergonomics to Leptos development.
Gesture-driven book navigation, context-driven state, and a PWA install prompt. All type-safe, all Rust. Built for Leptos 0.9.
Features
<Folio>— Page through a list with swipe, trackpad, mouse drag, and keyboard<FolioNav>— Prev/next buttons with a page counter and auto-disable at the ends<FolioTabs>— A tab bar that jumps to any pageuse_folio_context()— Drive or read navigation from any descendant component<InstallPrompt>— "Add to Home Screen" prompt for iOS and Chrome/Android- Gesture toolkit —
resolve(),SwipeDir,SwipeConfigfor rolling your own - Animated transitions — Slide-in animations keyed to turn direction
Quick Start
[]
= { = "0.9.0-alpha", = ["csr"] }
= "0.1"
use *;
use *;
Swipe left/right, press arrow keys, scroll horizontally on a trackpad, or click the buttons. It all just works.
Components
<Folio>
The paginated container. Renders one item at a time and owns all navigation state.
| Prop | Type | Default | Notes |
|---|---|---|---|
items |
Signal<Vec<T>> |
— | Items to page through |
render |
impl Fn(T) -> impl IntoView + Clone + Send + Sync |
— | Renders the visible item |
threshold |
f64 |
60.0 |
Min swipe distance in px |
inject_css |
bool |
true |
Inject the built-in stylesheet |
on_page_change |
Option<Arc<dyn Fn(usize) + Send + Sync>> |
None |
Called after each turn with the new index |
empty_fallback |
Option<ChildrenFn> |
None |
Shown when items is empty |
children |
Option<Children> |
None |
Rendered below the page (e.g. <FolioNav/>) |
T must be Clone + Send + Sync + 'static.
<FolioNav>
Prev/next buttons plus a current / total counter. Must be a descendant of <Folio>.
| Prop | Type | Default |
|---|---|---|
prev_label |
String |
"←" |
next_label |
String |
"→" |
<FolioNav prev_label="‹ before".to_string next_label="after ›".to_string/>
<FolioTabs>
A tab bar. Unlike FolioNav, it does not read context implicitly — you pass it the active index and a select callback, so you can wire it to a Folio (via use_folio_context()) or to your own state.
| Prop | Type |
|---|---|
tabs |
Vec<&'static str> |
active |
ReadSignal<usize> |
on_select |
Arc<dyn Fn(usize) + Send + Sync> |
let ctx = use_folio_context;
let go_to = ctx.go_to.clone;
view!
<InstallPrompt>
A PWA "install" / "Add to Home Screen" prompt. Shows iOS instructions on iOS, listens for beforeinstallprompt on Chrome/Android, and stays silent once installed or dismissed (remembered in localStorage).
| Prop | Type | Default |
|---|---|---|
title |
String |
"Install this app" |
description |
String |
"Add it to your home screen for the best experience." |
inject_css |
bool |
true |
title/description apply to the Chrome/Android prompt; iOS shows the standard "Add to Home Screen" instructions.
Driving navigation: use_folio_context()
Any descendant of <Folio> can call use_folio_context() to get a FolioContext:
You navigate by calling the closures, and read state from the signals:
let ctx = use_folio_context;
let go_next = ctx.go_next.clone;
view!
TurnDir has exactly two variants — Forward and Backward — and is reported via last_dir so you can react to (or animate) the direction of the most recent turn.
Gestures
<Folio> recognizes these out of the box:
| Input | Result |
|---|---|
| Swipe / drag right, scroll right | Next page |
| Swipe / drag left, scroll left | Previous page |
ArrowRight / ArrowDown / PageDown / Space |
Next page |
ArrowLeft / ArrowUp / PageUp |
Previous page |
To build your own recognizer, use the gesture module directly:
use ;
match resolve
SwipeConfig (threshold, keyboard, mouse) exists as a builder for your own handlers. Note it is not yet wired into <Folio> — Folio currently takes only threshold. See Roadmap.
Styling
Default styles ship in the FOLIO_CSS constant and are injected automatically. Disable that and bring your own:
<Folio inject_css=false items=cards render=render>
<style></style> // or your own
<FolioNav/>
</Folio>
Key classes: .folio, .folio-page-slot, .folio-page, .folio-enter-left, .folio-enter-right, .folio-nav, .folio-nav-btn, .folio-nav-counter, .folio-tabs, .folio-tab, .folio-tab.active. The install prompt ships its own INSTALL_CSS.
Examples
Each is a standalone Trunk app under examples/:
| Example | Shows |
|---|---|
basic-folio |
The minimal Folio + FolioNav |
gesture-demo |
resolve() live, plus a custom nav bar from FolioContext |
onboarding-tour |
FolioTabs wired to context + footer nav — the real-world shape |
Documentation
- Full Guide — every component, prop, and pattern
- Cookbook — task-focused recipes
- API docs — rustdoc
Roadmap
- Wire
SwipeConfig(keyboard/mouse toggles) into<Folio> - Carousel/wrap-around mode
- Virtualization for very large lists
See CHANGELOG.md for release history.
License
Dual-licensed under MIT or Apache 2.0. Take your pick.
Built for developers who think UI should be a joy, not a chore.