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:

  • MyCardProps struct with builder pattern
  • my_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

CategoryPatternExample
Visual state#[signal] — always reactivevariant, disabled, size
Content#[signal] with Option<Dom>content, header, label
CallbacksStatic Box<dyn Fn(...)>on_click, on_close, on_submit
ValuesTrait object or Mutable wrappervalue: dyn InputValueWrapper
ExtensionAuto-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(...)
})
}