Claude-skill-registry dioxus-component-patterns

Master Dioxus component patterns and reactive state management. Use when building Dioxus components, managing component state, or implementing component interactions.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dioxus-component-patterns" ~/.claude/skills/majiayu000-claude-skill-registry-dioxus-component-patterns && rm -rf "$T"
manifest: skills/data/dioxus-component-patterns/SKILL.md
source content

Dioxus Component Patterns

Overview

This skill covers component patterns in Dioxus, including component structure, props, state management, event handling, and component composition.

Component Structure

Basic Component

use dioxus::prelude::*;

#[component]
fn ComponentName() -> Element {
    rsx! {
        div {
            "Hello, World!"
        }
    }
}

Component with State

#[component]
fn Counter() -> Element {
    let mut count = use_signal(|| 0);
    
    rsx! {
        div {
            h1 { "Count: {count()}" }
            button {
                onclick: move |_| count.set(count() + 1),
                "Increment"
            }
        }
    }
}

Props Pattern

Simple Props

#[component]
fn Greeting(name: String) -> Element {
    rsx! {
        div {
            "Hello, {name}!"
        }
    }
}

Props with Event Handlers

#[component]
fn ReminderCard(
    reminder: Reminder,
    on_toggle: EventHandler<String>,
    on_delete: EventHandler<String>,
) -> Element {
    let reminder_id = reminder.id.clone();
    
    rsx! {
        div {
            class: "reminder-card",
            h3 { "{reminder.title}" }
            button {
                onclick: move |_| on_toggle.call(reminder_id.clone()),
                "Toggle"
            }
            button {
                onclick: move |_| on_delete.call(reminder_id),
                "Delete"
            }
        }
    }
}

State Management Patterns

Local Component State

#[component]
fn Form() -> Element {
    let mut title = use_signal(|| String::new());
    let mut description = use_signal(|| String::new());
    
    rsx! {
        input {
            value: "{title()}",
            oninput: move |e| title.set(e.value())
        }
    }
}

Shared State via Props

#[component]
fn App() -> Element {
    let mut reminders = use_signal(|| load_reminders());
    
    rsx! {
        ReminderList {
            reminders: reminders(),
            on_update: move |new_reminders| {
                reminders.set(new_reminders);
            }
        }
    }
}

Derived State

#[component]
fn ReminderStats(reminders: Vec<Reminder>) -> Element {
    let total = reminders.len();
    let completed = reminders.iter().filter(|r| r.completed).count();
    let active = total - completed;
    
    rsx! {
        div {
            "Total: {total}, Active: {active}, Completed: {completed}"
        }
    }
}

Event Handling Patterns

Inline Handlers

rsx! {
    button {
        onclick: move |_| {
            count.set(count() + 1);
        },
        "Click"
    }
}

Handler with Event Data

rsx! {
    input {
        oninput: move |e| {
            title.set(e.value());
        }
    }
}

Handler with Cloned Data

#[component]
fn ReminderItem(reminder: Reminder, on_delete: EventHandler<String>) -> Element {
    let reminder_id = reminder.id.clone();
    
    rsx! {
        button {
            onclick: move |_| {
                on_delete.call(reminder_id);
            },
            "Delete"
        }
    }
}

Conditional Rendering

Simple If

rsx! {
    if show_form() {
        AddReminderForm { on_add: move |r| { /* ... */ } }
    }
}

If-Else

rsx! {
    if reminders().is_empty() {
        div { "No reminders yet" }
    } else {
        ReminderList { reminders: reminders() }
    }
}

Ternary-like Pattern

rsx! {
    div {
        class: if is_active() { "active" } else { "inactive" },
        "Content"
    }
}

List Rendering

Simple List

rsx! {
    for reminder in reminders().iter() {
        ReminderCard {
            reminder: reminder.clone(),
            on_toggle: move |id| { /* ... */ },
        }
    }
}

List with Filter

rsx! {
    for reminder in reminders().iter().filter(|r| !r.completed) {
        ReminderCard { reminder: reminder.clone() }
    }
}

List with Index

rsx! {
    for (index, reminder) in reminders().iter().enumerate() {
        ReminderCard {
            reminder: reminder.clone(),
            index: index,
        }
    }
}

Component Composition

Container Component

#[component]
fn App() -> Element {
    let mut reminders = use_signal(|| load_reminders());
    let mut show_form = use_signal(|| false);
    
    rsx! {
        div {
            Header {
                on_new_click: move |_| show_form.set(true)
            }
            if show_form() {
                AddReminderForm {
                    on_add: move |r| { /* ... */ },
                    on_cancel: move |_| show_form.set(false),
                }
            }
            ReminderList {
                reminders: reminders(),
                on_update: move |new| reminders.set(new),
            }
        }
    }
}

Presentational Component

#[component]
fn ReminderList(
    reminders: Vec<Reminder>,
    on_update: EventHandler<Vec<Reminder>>,
) -> Element {
    rsx! {
        div {
            class: "reminder-list",
            for reminder in reminders.iter() {
                ReminderCard {
                    reminder: reminder.clone(),
                    on_toggle: move |id| {
                        let mut updated = reminders.clone();
                        // Update logic
                        on_update.call(updated);
                    },
                }
            }
        }
    }
}

Form Patterns

Controlled Input

#[component]
fn TextInput(value: String, on_change: EventHandler<String>) -> Element {
    rsx! {
        input {
            value: "{value}",
            oninput: move |e| on_change.call(e.value())
        }
    }
}

Form with Validation

#[component]
fn AddReminderForm(on_add: EventHandler<Reminder>) -> Element {
    let mut title = use_signal(|| String::new());
    let mut error = use_signal(|| None::<String>);
    
    rsx! {
        div {
            input {
                value: "{title()}",
                oninput: move |e| {
                    title.set(e.value());
                    if e.value().is_empty() {
                        error.set(Some("Title is required".to_string()));
                    } else {
                        error.set(None);
                    }
                }
            }
            if let Some(err) = error() {
                span { class: "error", "{err}" }
            }
            button {
                disabled: title().is_empty(),
                onclick: move |_| {
                    if !title().is_empty() {
                        on_add.call(Reminder { /* ... */ });
                        title.set(String::new());
                    }
                },
                "Add"
            }
        }
    }
}

Best Practices

DO:

  • ✅ Keep components small and focused
  • ✅ Use descriptive component names
  • ✅ Pass only necessary props
  • ✅ Use
    EventHandler
    for callbacks
  • ✅ Clone only what's needed in closures
  • ✅ Use conditional rendering for UI states
  • ✅ Use iterators for list rendering

DON'T:

  • ❌ Don't create signals in render loops
  • ❌ Don't mutate state directly (use
    .set()
    )
  • ❌ Don't pass entire state to child components
  • ❌ Don't create unnecessary clones
  • ❌ Don't use indices when iterators work
  • ❌ Don't mix concerns in components

Common Patterns

Loading State

let mut loading = use_signal(|| false);

rsx! {
    if loading() {
        div { "Loading..." }
    } else {
        ReminderList { reminders: reminders() }
    }
}

Error State

let mut error = use_signal(|| None::<String>);

rsx! {
    if let Some(err) = error() {
        div { class: "error", "{err}" }
    }
}

Toggle Pattern

let mut is_open = use_signal(|| false);

rsx! {
    button {
        onclick: move |_| is_open.set(!is_open()),
        if is_open() { "Close" } else { "Open" }
    }
    if is_open() {
        div { "Content" }
    }
}

Resources