Expand description
Reinhardt Pages - WASM-based Frontend Framework
A Django-inspired frontend framework for Reinhardt that preserves the benefits of Django templates while leveraging WebAssembly for modern interactivity.
Use reactive::batch to group related reactive writes into one update
cycle. Async Action handles can be connected to OptimisticState with
Action::with_optimistic so failed mutations automatically roll back
optimistic UI state.
§Features
- Fine-grained Reactivity: Leptos/Solid.js-style Signal system with React-aligned hooks
- Hybrid Rendering: SSR + Client-side Hydration for optimal performance and SEO
- Django-like API: Familiar patterns for Reinhardt developers
- Boundaries: Suspense and error boundaries for async UI states
§React-aligned hook signatures (v0.2, Refs #4195)
use_effect, use_layout_effect, use_memo, use_callback, and
use_callback_with take an explicit dependency tuple as the second
argument:
use reinhardt_pages::prelude::*;
let count = Signal::new(0_i32);
let count_for_effect = count.clone();
let _eff = use_effect(
move || {
println!("count = {}", count_for_effect.get());
None::<fn()>
},
(count.clone(),),
);Closures run with no active reactive Observer (“Option A”), so
Signal::get inside does NOT auto-subscribe — subscriptions derive
exclusively from the deps tuple. Pass () for mount-only effects.
For a concept-by-concept mapping from React to Reinhardt Pages, see
docs/react_to_reinhardt.md in this crate.
- Low-level Only: Built on wasm-bindgen, web-sys, and js-sys (no high-level framework dependencies)
- Security First: Built-in CSRF protection, XSS prevention, and session management
§Architecture
This framework consists of several key modules:
reactive: Fine-grained reactivity system (Signal, Effect, Memo)dom: DOM abstraction layerbuilder: HTML element builder APIcomponent: Component system with IntoPage trait, Head managementform: Django Form integrationform_state: Typeduse_formruntime statecsrf: CSRF protectionauth: Authentication integrationapi: API client with Django QuerySet-like interfaceserver_fn: Server Functions (RPC)ssr: Server-side rendering with Head supporthydration: Client-side hydrationrouter: Client-side routing (reinhardt-urls compatible)static_resolver: Static file URL resolution (collectstatic support)
§Forms
form! owns static form definition: field names, widgets, labels,
rendering metadata, and server function binding. use_form owns typed
runtime behavior: value signals, dirty/touched state, validation errors,
loading/success state, reset, and submit orchestration.
Create a form! generated form and attach runtime behavior with
use_form:
use reinhardt_pages::{form, use_form};
let login_form = form! {
name: LoginForm,
action: "/login",
fields: {
username: CharField { initial: String::new() }
password: CharField { initial: String::new() }
}
};
let runtime = use_form(&login_form).build();
runtime.set_value(login_form.username_field(), "ada".to_string());Use ambient_arguments for non-field values supplied from surrounding
context. The old strip_arguments DSL name remains as a deprecated alias.
CSRF should be supplied by #[server_fn] client stubs through the
X-CSRFToken header rather than as a server function business argument.
§Macros
page!: JSX-like macro for defining view componentshead!: JSX-like macro for defining HTML head sections
§Example
§Basic Component
use reinhardt_pages::{Signal, Page, page};
fn counter() -> Page {
let count = Signal::new(0);
page!(|count: Signal<i32>| {
div {
p { { format!("Count: {}", count.get()) } }
}
})(count)
}§With Head Section
use reinhardt_pages::{head, page, Page, resolve_static};
fn home_page() -> Page {
let page_head = head!(|| {
title { "Home - My App" }
meta { name: "description", content: "Welcome to my app" }
link { rel: "stylesheet", href: resolve_static("css/main.css") }
});
page! {
#head: page_head,
|| {
div { class: "container",
h1 { "Welcome Home" }
}
}
}()
}§WebSocket Integration
The use_websocket hook provides reactive WebSocket connections:
use reinhardt_pages::reactive::hooks::{use_websocket, use_effect, UseWebSocketOptions};
use reinhardt_pages::reactive::hooks::{ConnectionState, WebSocketMessage};
fn chat_component() -> Page {
// Establish WebSocket connection
let ws = use_websocket("ws://localhost:8000/ws/chat", UseWebSocketOptions::default());
// Monitor connection state reactively
use_effect(
{
let ws = ws.clone();
move || {
match ws.connection_state().get() {
ConnectionState::Open => log!("Connected to chat"),
ConnectionState::Closed => log!("Disconnected from chat"),
ConnectionState::Error(e) => log!("Connection error: {}", e),
_ => {}
}
None::<fn()>
}
},
(),
);
// Handle incoming messages
use_effect(
{
let ws = ws.clone();
move || {
if let Some(WebSocketMessage::Text(text)) = ws.latest_message().get() {
log!("Received: {}", text);
}
None::<fn()>
}
},
(),
);
page!(|| {
div {
button {
@click: move |_| {
ws.send_text("Hello, server!".to_string()).ok();
},
"Send Message"
}
}
})()
}Note: WebSocket functionality is WASM-only. On the server side (SSR),
use_websocket returns a no-op handle with connection state always set to Closed.
Re-exports§
pub use api::ApiModel;pub use api::ApiQuerySet;pub use api::Filter;pub use api::FilterOp;pub use auth::AuthData;pub use auth::AuthError;pub use auth::AuthState;pub use auth::auth_state;pub use builder::attributes::AriaAttributes;pub use builder::attributes::BooleanAttributes;pub use builder::html::a;pub use builder::html::div;pub use builder::html::form;pub use builder::html::h1;pub use builder::html::h2;pub use builder::html::h3;pub use builder::html::img;pub use builder::html::input;pub use builder::html::li;pub use builder::html::ol;pub use builder::html::option;pub use builder::html::p;pub use builder::html::select;pub use builder::html::span;pub use builder::html::textarea;pub use builder::html::ul;pub use callback::Callback;pub use callback::IntoEventHandler;pub use callback::event_handler;pub use callback::into_event_handler;pub use component::BoundaryError;pub use component::Component;pub use component::ErrorBoundary;pub use component::ErrorTracker;pub use component::PageExt;pub use component::Props;pub use component::ResourceTracker;pub use component::SuspenseBoundary;pub use csrf::CsrfManager;pub use csrf::get_csrf_token;pub use dom::Document;pub use dom::Element;pub use dom::EventHandle;pub use dom::document;pub use dom::document;pub use form::FormBinding;pub use form::FormComponent;pub use form_generated::StaticFieldMetadata;pub use form_generated::StaticFormMetadata;pub use form_state::FieldError;pub use form_state::FieldState;pub use form_state::FocusError;pub use form_state::FormEvent;pub use form_state::FormRuntimeSource;pub use form_state::FormState;pub use form_state::FormSubscription;pub use form_state::FormValidationError;pub use form_state::NoDeps;pub use form_state::ResetOnDeps;pub use form_state::RevalidateOn;pub use form_state::UseFormBuilder;pub use form_state::UseFormReturn;pub use form_state::UseFormSubmitOutcome;pub use form_state::use_form;pub use hydration::HydrationContext;pub use hydration::HydrationError;pub use hydration::hydrate;pub use reactive::Resource;pub use reactive::ResourceState;pub use reactive::use_resource;pub use app::ClientLauncher;pub use app::LaunchCtx;pub use app::PathCtx;pub use app::PathParams;pub use reactive::Action;pub use reactive::ActionPhase;pub use reactive::use_action;pub use reactive::Dispatch;pub use reactive::OptimisticState;pub use reactive::Ref;pub use reactive::SetState;pub use reactive::TransitionState;pub use reactive::use_callback;pub use reactive::use_context;pub use reactive::use_debug_value;pub use reactive::use_deferred_value;pub use reactive::use_effect;pub use reactive::use_effect_event;Deprecated pub use reactive::use_id;pub use reactive::use_layout_effect;pub use reactive::use_memo;pub use reactive::use_optimistic;pub use reactive::use_reducer;pub use reactive::use_ref;pub use reactive::use_state;pub use reactive::use_sync_external_store;pub use reactive::use_transition;pub use router::Link;pub use reactive::hooks::router::RouterHandle;pub use reactive::hooks::router::use_router;pub use server_fn::ServerFn;pub use server_fn::ServerFnError;pub use server_fn::parse_server_error_message;pub use ssr::SsrState;pub use ssr::SsrOptions;pub use ssr::SsrRenderer;pub use static_resolver::init_static_resolver;pub use static_resolver::is_initialized;pub use static_resolver::resolve_static;pub use reinhardt_pages_ast as ast;Deprecated
Modules§
- api
- API Client with Django QuerySet-like Interface
- app
- WASM client application launcher.
- auth
- Authentication State Management for Client-side WASM
- builder
- HTML Builder API
- callback
- Callback types and event handler conversion traits.
- component
- Component System for reinhardt-pages
- csrf
- CSRF Protection for Client-side WASM
- dom
- DOM Abstraction Layer
- form
- Form Integration for Reinhardt WASM (Week 5 Day 3-4)
- form_
generated - Static Metadata Types for form! Macro Generated Code
- form_
state - Runtime behavior for
form!generated forms. - hydration
- Hydration Module for reinhardt-pages
- integ
- Integration modules for special macros.
- logging
- Logging abstraction layer for reinhardt-pages
- platform
- Platform abstraction for WASM and native targets.
- prelude
- Unified prelude for reinhardt-pages.
- reactive
- Reactive System
- router
- Client-Side Router for reinhardt-pages
- server_
fn - Server Functions (RPC)
- spawn
- Backward-compatibility re-export of task-spawning utilities.
- ssr
- Server-Side Rendering (SSR) Infrastructure
- static_
resolver - Static File URL Resolver
- tables
- Table utilities for reinhardt-pages (django-tables2 equivalent)
Macros§
- debug_
log - No-op debug_log when conditions are not met
- error_
log - Logs an error message (requires
debug_assertions) - form
- Form component macro
- head
- Head section macro
- info_
log - Logs an info message (requires
debug_assertions) - nav_
diag - No-op
nav_diagoutside WASM dev builds. - nav_
diag_ dom - No-op
nav_diag_domwhen thenav-diag-domfeature is disabled or on non-WASM targets. - page
- Derives typed field signals and form traits for a form values struct. Page component macro
- warn_
log - Logs a warning message (requires
debug_assertions)
Structs§
- Context
- A type-safe context identifier.
- Context
Guard - A RAII guard that removes a context value when dropped.
- Dummy
Event - Dummy event type for non-WASM environments.
- Effect
- A reactive effect that automatically re-runs when its dependencies change
- Field
Metadata - Serializable field metadata for client-side rendering (Week 5 Day 1)
- Form
Metadata - Serializable form metadata for client-side rendering (Week 5 Day 1)
- Head
- Represents the complete HTML
<head>section. - LinkTag
- Represents an HTML
<link>tag. - Memo
- A memoized reactive computation that caches its result
- MetaTag
- Represents an HTML
<meta>tag. - Page
Element - Represents a DOM element in the view tree.
- Script
Tag - Represents an HTML
<script>tag. - Signal
- A reactive signal that holds a value and tracks dependencies
- Style
Tag - Represents an HTML
<style>tag with inline CSS.
Enums§
- Event
Type - Common DOM event types
- Navigation
Type - The type of navigation that occurred.
- Page
- A unified representation of renderable content.
- Widget
- Field widget type that determines HTML rendering.
Traits§
- Into
Page - Trait for types that can be converted into a Page.
Functions§
- create_
context - Creates a new context.
- get_
context - Gets the current value from a context.
- provide_
context - Provides a value for a context.
- remove_
context - Removes the most recently provided value for a context.