name: dwind-component description: Use when the user asks to create a component, build a UI element, use a dwui widget, references the #[component] macro, or asks about component patterns in the dwind/dominator stack. version: 1.0.0
Dwind Component Patterns
Build reactive, type-safe UI components using the dominator + dwind + futures-signals stack.
The #[component] Macro
Declare a component struct. The macro generates a props builder and an invocation macro.
#![allow(unused)] fn main() { use futures_signals_component_macro::component; #[component(render_fn = my_card)] struct MyCard { #[signal] #[default(None)] content: Option<Dom>, #[signal] #[default("".to_string())] title: String, #[default(Box::new(|_: events::Click| {}))] on_click: dyn Fn(events::Click) -> () + 'static, #[signal] #[default(false)] disabled: bool, } }
This generates:
MyCardPropsstruct with builder patternmy_card!({ .title("Hello").content(text("Body")) })macro for ergonomic usage- Each
#[signal]field gets both.prop(value)and.prop_signal(signal)setters
Render Function
#![allow(unused)] fn main() { pub fn my_card(props: MyCardProps) -> Dom { let MyCardProps { content, title, on_click, disabled, apply } = props; // Broadcast signals used in multiple places let disabled = disabled.broadcast(); html!("div", { .dwclass!("p-4 bg-gray-900 rounded-lg shadow-lg transition-all") // Reactive styling .style_signal("opacity", disabled.signal().map(|d| if d { "0.5" } else { "1" })) .style_signal("pointer-events", disabled.signal().map(|d| if d { "none" } else { "auto" })) // Reactive text content .child(html!("h3", { .dwclass!("text-lg font-bold mb-2 text-white") .text_signal(title) })) // Optional DOM content .child_signal(content) .event(move |e: events::Click| { (on_click)(e); }) // Extension point for consumers .apply_if(apply.is_some(), move |b| b.apply(apply.unwrap())) }) } }
Prop Rules
| Category | Pattern | Example |
|---|---|---|
| Visual state | #[signal] — always reactive | variant, disabled, size |
| Content | #[signal] with Option<Dom> | content, header, label |
| Callbacks | Static Box<dyn Fn(...)> | on_click, on_close, on_submit |
| Values | Trait object or Mutable wrapper | value: dyn InputValueWrapper |
| Extension | Auto-generated by #[component] | apply field (always present) |
Critical Rules
Broadcast signals used in multiple places
A signal can only be consumed once. If you need the same signal in two or more .style_signal() / .dwclass_signal!() / .child_signal() calls, broadcast it first:
#![allow(unused)] fn main() { let disabled = disabled.broadcast(); // Now call disabled.signal() as many times as needed }
Wrap callbacks in Rc for multiple closures
Box<dyn Fn()> is not Clone. If a callback is used in multiple event handlers:
#![allow(unused)] fn main() { let on_close = std::rc::Rc::new(on_close); .event({ let on_close = on_close.clone(); move |_: events::Click| { (on_close)(); } }) .global_event({ let on_close = on_close.clone(); move |e: events::KeyDown| { if e.key() == "Escape" { (on_close)(); } }}) }
Box delegation impl
When a component field uses dyn SomeTrait, the generated code stores it as Box<dyn SomeTrait>. Add a delegation impl:
#![allow(unused)] fn main() { impl<T: ToggleValue + ?Sized> ToggleValue for Box<T> { fn get_signal(&self) -> LocalBoxSignal<'static, bool> { (**self).get_signal() } fn toggle(&self) { (**self).toggle() } } }
Consumer crate macro imports
Consumer crates must import macros explicitly:
#![allow(unused)] fn main() { #[macro_use] extern crate dwind_macros; // for dwclass! #[macro_use] extern crate my_design_system; // for component macros (my_card!, etc.) }
The apply extension point
Every #[component] struct gets an auto-generated apply field. Use apply_if in the render function:
#![allow(unused)] fn main() { .apply_if(apply.is_some(), move |b| b.apply(apply.unwrap())) }
Consumers customize the root element:
#![allow(unused)] fn main() { my_card!({ .title("Custom") .apply(|b| b.dwclass!("border border-blue-500")) }) }
Available Component Library
dwui is the published component library built on the dwind stack. Read references/component-catalog.md for the full catalog with props and usage examples.
Components: Button, Modal, TextInput, Select, Slider, Card, Heading, List
To see the full implementation of any component, read its source file:
/home/mmy/repos/oss/dominator-css-bindgen/crates/dwui/src/components/
Mixins Pattern
Reusable DomBuilder transforms for shared visual effects:
#![allow(unused)] fn main() { pub fn glass_surface(level: SurfaceLevel) -> impl FnOnce(DomBuilder<HtmlElement>) -> DomBuilder<HtmlElement> { move |b| { b.style("background", match level { SurfaceLevel::Base => "var(--my-bg)", SurfaceLevel::Elevated => "var(--my-bg-elevated)", }) .style("box-shadow", "var(--my-shadow)") } } // Usage: html!("div", { .apply(glass_surface(SurfaceLevel::Elevated)) .child(...) }) }