update the dioxus cratesio readme, add many more examples (#5501)

* update the dioxus cratesio readme

* fix example

* fix header

* add a bunch more examples
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2026-04-21 17:07:48 -07:00
1 parent 74e7d20f80
commit cd54f5953b
30 files changed
+1403 -235

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
//! Checkboxes and radio groups.
//!
//! Checkboxes expose their checked state through the FormEvent — read `evt.checked()`.
//! Radio groups share a `name` attribute so only one option can be selected at a time;
//! the selected value comes through `evt.value()`.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut subscribed = use_signal(|| false);
let mut color = use_signal(|| "red".to_string());
rsx! {
h2 { "Checkbox" }
label {
input {
r#type: "checkbox",
checked: subscribed(),
oninput: move |evt| subscribed.set(evt.checked()),
}
" Subscribe to updates"
}
p { "Subscribed: {subscribed}" }
h2 { "Radio group" }
// Radio inputs sharing a `name` are mutually exclusive
for value in ["red", "green", "blue"] {
label {
input {
r#type: "radio",
name: "color",
value,
checked: color() == value,
oninput: move |evt| color.set(evt.value()),
}
" {value}"
}
}
p { color: "{color}", "Chosen color: {color}" }
}
}
+52
View File
@@ -0,0 +1,52 @@
//! Accepting children on a component.
//!
//! Any component can take a `children: Element` prop. Anything nested inside the component
//! in `rsx!` is passed in as `children`. This is how you build reusable layout and container
//! components like cards, modals, or page shells.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
rsx! {
Card {
h2 { "A card" }
p { "Any rsx! can go inside — including other components." }
}
Card {
p { "A second card with different children." }
button { "Click me" }
}
Section { title: "Reusable layout",
p { "The section component takes both a title prop and children." }
}
}
}
#[component]
fn Card(children: Element) -> Element {
rsx! {
div {
border: "1px solid #ccc",
border_radius: "8px",
padding: "12px",
margin: "8px 0",
{children}
}
}
}
#[component]
fn Section(title: String, children: Element) -> Element {
rsx! {
section {
h3 { "{title}" }
{children}
}
}
}
+46
View File
@@ -0,0 +1,46 @@
//! Defining and using components.
//!
//! A component is just a function annotated with `#[component]` that returns an `Element`.
//! Its arguments are its props — Dioxus generates a builder so you can construct the
//! component inside `rsx!` with a struct-like syntax.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
rsx! {
h1 { "Greetings" }
Greeting { name: "Alice" }
Greeting { name: "Bob", excited: true }
Greeting { name: "Charlie", excited: false }
h2 { "User cards" }
UserCard { name: "Ada Lovelace", role: "Mathematician" }
UserCard { name: "Grace Hopper", role: "Computer Scientist" }
}
}
// Components must start with an uppercase letter.
// Props are taken as named arguments — `name` is required, and `excited` has a default.
#[component]
fn Greeting(name: String, #[props(default)] excited: bool) -> Element {
let punctuation = if excited { "!" } else { "." };
rsx! {
p { "Hello, {name}{punctuation}" }
}
}
// Components can take any Clone + PartialEq type as a prop.
#[component]
fn UserCard(name: String, role: String) -> Element {
rsx! {
div { border: "1px solid #ccc", padding: "8px", margin: "4px",
strong { "{name}" }
" — "
em { "{role}" }
}
}
}
@@ -0,0 +1,52 @@
//! Conditional rendering.
//!
//! `if`, `else`, and `match` all work directly inside `rsx!`. You can also return different
//! `rsx!` blocks early from a component function. Elements outside of a conditional will
//! render normally — only the branched portion swaps as state changes.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
#[derive(PartialEq, Clone, Copy)]
enum Status {
Loading,
Ready,
Error,
}
fn app() -> Element {
let mut logged_in = use_signal(|| false);
let mut status = use_signal(|| Status::Loading);
rsx! {
h1 { "Conditional rendering" }
// `if/else` chains render the matching branch
if logged_in() {
p { "Welcome back!" }
} else {
p { "Please log in." }
}
button { onclick: move |_| logged_in.toggle(), "Toggle login" }
// `match` works too — each arm returns rsx!
div {
match status() {
Status::Loading => rsx! { p { "⏳ Loading..." } },
Status::Ready => rsx! { p { "✅ Ready!" } },
Status::Error => rsx! { p { "❌ Something went wrong." } },
}
}
button { onclick: move |_| status.set(Status::Loading), "Loading" }
button { onclick: move |_| status.set(Status::Ready), "Ready" }
button { onclick: move |_| status.set(Status::Error), "Error" }
// `if` without an `else` is also valid — nothing renders when false
if logged_in() && status() == Status::Ready {
p { "You are logged in and the app is ready." }
}
}
}
+22
View File
@@ -0,0 +1,22 @@
//! The classic counter — the "hello world" of reactive UI.
//!
//! `use_signal` creates a reactive value. Whenever the signal changes, any part of the UI
//! that reads it will re-render automatically. Signals are copy-cheap, so you can freely
//! move them into event handlers and closures.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut count = use_signal(|| 0);
rsx! {
h1 { "Count: {count}" }
button { onclick: move |_| count += 1, "Increment" }
button { onclick: move |_| count -= 1, "Decrement" }
button { onclick: move |_| count.set(0), "Reset" }
}
}
@@ -0,0 +1,54 @@
//! Applying classes and styles based on state.
//!
//! Any attribute can interpolate signals and expressions using `"{value}"` formatting.
//! For `class:`, you can also build the class string inline with if/else, and individual
//! CSS properties can be set as named attributes like `color:` or `background_color:`.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut is_active = use_signal(|| false);
let mut size = use_signal(|| 16);
// Build a class string from state
let classes = if is_active() { "btn active" } else { "btn" };
rsx! {
// CSS properties are plain named attributes — snake_case in Rust, kebab-case in CSS
style { {STYLE} }
button {
class: classes,
onclick: move |_| is_active.toggle(),
"Toggle active (currently {is_active})"
}
// Or compute the class string ahead of time for anything more complex
div {
class: if is_active() { "box highlight" } else { "box" },
"This box changes class with the button."
}
hr {}
// Style values can be driven directly by signals
p {
font_size: "{size}px",
color: if size() > 24 { "crimson" } else { "black" },
"Font size: {size}px"
}
button { onclick: move |_| size += 2, "Bigger" }
button { onclick: move |_| size -= 2, "Smaller" }
}
}
const STYLE: &str = r#"
.btn { padding: 6px 12px; border: 1px solid #888; border-radius: 4px; }
.btn.active { background: #4CAF50; color: white; border-color: #2E7D32; }
.box { padding: 12px; margin-top: 12px; border: 1px dashed #999; }
.box.highlight { background: #fff3cd; border-color: #d39e00; }
"#;
@@ -0,0 +1,45 @@
//! Passing event handlers as props.
//!
//! Components can accept callbacks using `EventHandler<T>`, which is Dioxus's equivalent
//! of React's `onClick` prop or a closure. Call the handler with `.call(value)` from the
//! child; the parent decides what happens when the event fires.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut log = use_signal(Vec::<String>::new);
rsx! {
h1 { "Custom event handlers" }
// The parent owns the handler — the child just fires it
FancyButton {
label: "Say hi",
onpress: move |name: String| log.write().push(format!("Hi, {name}!")),
}
FancyButton {
label: "Say bye",
onpress: move |name: String| log.write().push(format!("Bye, {name}!")),
}
ul {
for entry in log.iter() {
li { "{entry}" }
}
}
}
}
#[component]
fn FancyButton(label: String, onpress: EventHandler<String>) -> Element {
rsx! {
button {
onclick: move |_| onpress.call(label.clone()),
"{label}"
}
}
}
+88
View File
@@ -0,0 +1,88 @@
//! Handling form submission and deserializing into a struct.
//!
//! `evt.parsed_values::<T>()` takes the form's named inputs and deserializes them straight
//! into your own type via serde. This skips the chore of pulling each field out of the
//! values map by hand — just define a struct whose fields match the `name` attributes,
//! derive `Deserialize`, and you're done.
use dioxus::prelude::*;
use serde::Deserialize;
fn main() {
dioxus::launch(app);
}
// Field names must match the `name` attribute on each input
#[derive(Deserialize, Debug, Clone)]
struct Signup {
username: String,
email: String,
role: String,
// Checkboxes are present in the form data only when checked, so model optional fields
// with Option<String>. Use `#[serde(default)]` so the field doesn't fail to deserialize
// when the input is missing.
#[serde(default)]
newsletter: Option<String>,
}
fn app() -> Element {
let mut submitted = use_signal(|| None::<Signup>);
rsx! {
h1 { "Sign up" }
form {
onsubmit: move |evt| {
// prevent_default stops the browser from navigating the page on submit
evt.prevent_default();
// Deserialize directly into our struct
match evt.parsed_values::<Signup>() {
Ok(signup) => submitted.set(Some(signup)),
Err(err) => eprintln!("failed to parse form: {err}"),
}
},
div {
label { r#for: "username", "Username: " }
input { r#type: "text", id: "username", name: "username", required: true }
}
div {
label { r#for: "email", "Email: " }
input { r#type: "email", id: "email", name: "email", required: true }
}
div {
label { r#for: "role", "Role: " }
select { id: "role", name: "role",
option { value: "developer", "Developer" }
option { value: "designer", "Designer" }
option { value: "manager", "Manager" }
}
}
div {
label { r#for: "newsletter",
input { r#type: "checkbox", id: "newsletter", name: "newsletter", value: "yes" }
" Subscribe to newsletter"
}
}
button { r#type: "submit", "Submit" }
}
if let Some(signup) = submitted() {
h2 { "Welcome, {signup.username}!" }
ul {
li { "Email: {signup.email}" }
li { "Role: {signup.role}" }
li { "Newsletter: {signup.newsletter.is_some()}" }
}
details {
summary { "Raw struct" }
pre { "{signup:#?}" }
}
}
}
}
+43
View File
@@ -0,0 +1,43 @@
//! Controlled text inputs.
//!
//! To read text from an input, attach an `oninput` handler and call `evt.value()` to get the
//! current value as a `String`. Binding the `value:` attribute to a signal makes the input
//! "controlled" — the signal is the single source of truth, so clearing it clears the input.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut name = use_signal(String::new);
let mut message = use_signal(|| "Type something below.".to_string());
rsx! {
h1 { "Hello, {name}!" }
input {
r#type: "text",
placeholder: "Your name",
// Binding `value` to a signal makes this a controlled input
value: "{name}",
oninput: move |evt| name.set(evt.value()),
}
button {
onclick: move |_| name.set(String::new()),
"Clear"
}
hr {}
p { "{message}" }
textarea {
rows: 3,
cols: 40,
value: "{message}",
oninput: move |evt| message.set(evt.value()),
}
}
}
+53
View File
@@ -0,0 +1,53 @@
//! Rendering lists.
//!
//! Use a `for` loop inside `rsx!` to render a list from an iterator. When list items can
//! be reordered, added, or removed, provide a stable `key` so Dioxus can efficiently
//! reuse existing elements rather than re-rendering them all.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut items = use_signal(|| {
vec![
"apple".to_string(),
"banana".to_string(),
"cherry".to_string(),
]
});
let mut next_id = use_signal(|| 0);
rsx! {
h1 { "Grocery list" }
button {
onclick: move |_| {
let n = next_id();
next_id += 1;
items.push(format!("item {n}"));
},
"Add item"
}
button {
onclick: move |_| { items.pop(); },
"Remove last"
}
ul {
// The `key` is a stable identifier used to match up items across renders.
// Here we use the item's text, but in real apps prefer a database id.
for item in items.iter() {
li { key: "{item}", "{item}" }
}
}
// You can also use iterator adapters with .map() for anything more complex than a for loop
p { "Uppercased:" }
ul {
{items.iter().map(|item| rsx! { li { "{item.to_uppercase()}" } })}
}
}
}