# Component Cookbook Comprehensive examples for all vapora-leptos-ui components. ## Table of Contents - [Primitives](#primitives) - [Button](#button) - [Input](#input) - [Badge](#badge) - [Spinner](#spinner) - [Layout](#layout) - [Card](#card) - [Modal](#modal) - [Data](#data) - [Table](#table) - [Pagination](#pagination) - [StatCard](#statcard) - [Forms](#forms) - [FormField](#formfield) - [Validation](#validation) - [Feedback](#feedback) - [Toast](#toast) - [Navigation](#navigation) - [SpaLink](#spalink) - [Patterns](#patterns) - [Modal with Form](#modal-with-form) - [Table with Sorting and Pagination](#table-with-sorting-and-pagination) - [Dashboard with Stats](#dashboard-with-stats) --- ## Primitives ### Button ```rust use leptos::prelude::*; use vapora_leptos_ui::{Button, Variant, Size}; #[component] fn ButtonExamples() -> impl IntoView { let (count, set_count) = signal(0); view! {
// Primary button // Secondary button // Danger button // Ghost button // Loading button // Disabled button
} } ``` ### Input ```rust use leptos::prelude::*; use vapora_leptos_ui::Input; /// Extract value from input event (Leptos 0.8 helper) #[cfg(target_arch = "wasm32")] fn event_target_value(ev: &leptos::ev::Event) -> String { use wasm_bindgen::JsCast; ev.target() .and_then(|t| t.dyn_into::().ok()) .map(|input| input.value()) .unwrap_or_default() } #[component] fn InputExamples() -> impl IntoView { let (username, set_username) = signal(String::new()); let (password, set_password) = signal(String::new()); let (email, set_email) = signal(String::new()); view! {
// Text input // Password input // Email input // Disabled input // Display values
"Username: " {move || username.get()}
"Password: " {move || "*".repeat(password.get().len())}
"Email: " {move || email.get()}
} } ``` ### Badge ```rust use leptos::prelude::*; use vapora_leptos_ui::Badge; #[component] fn BadgeExamples() -> impl IntoView { view! {
"Active" "Pending" "Error" "Premium" "Archived"
} } ``` ### Spinner ```rust use leptos::prelude::*; use vapora_leptos_ui::{Spinner, Size}; #[component] fn SpinnerExamples() -> impl IntoView { view! {
"Small"
"Medium"
"Large"
} } ``` --- ## Layout ### Card ```rust use leptos::prelude::*; use vapora_leptos_ui::{Card, GlowColor, Badge}; #[component] fn CardExamples() -> impl IntoView { view! {
// Basic card

"Basic Card"

"Simple glassmorphism card"

// Hoverable card with glow

"Hoverable"

"Hover for effect"

// Card with content

"Project"

"Active"

"Multi-agent development platform"

"Rust" "Leptos"
} } ``` ### Modal ```rust use leptos::prelude::*; use vapora_leptos_ui::{Modal, Button, Variant}; #[component] fn ModalExample() -> impl IntoView { let (show_modal, set_show_modal) = signal(false); let (show_large_modal, set_show_large_modal) = signal(false); view! {
// Trigger buttons // Simple modal

"Confirmation"

"Are you sure you want to proceed?"

// Large modal with content

"Project Details"

"This is a large modal with more content."

"Modal features:"

  • "Press Escape to close"
  • "Click backdrop to close"
  • "Tab navigation trapped inside"
  • "Auto-focus on first focusable element"
} } ``` --- ## Data ### Table ```rust use leptos::prelude::*; use vapora_leptos_ui::{Table, TableColumn}; #[component] fn TableExample() -> impl IntoView { let columns = vec![ TableColumn::new("Name", "name").sortable(), TableColumn::new("Email", "email").sortable(), TableColumn::new("Status", "status").sortable(), TableColumn::new("Role", "role"), ]; let rows = vec![ vec![ "Alice Smith".to_string(), "alice@example.com".to_string(), "Active".to_string(), "Admin".to_string(), ], vec![ "Bob Johnson".to_string(), "bob@example.com".to_string(), "Inactive".to_string(), "User".to_string(), ], vec![ "Carol Williams".to_string(), "carol@example.com".to_string(), "Active".to_string(), "Moderator".to_string(), ], ]; view! { } } ``` ### Pagination ```rust use leptos::prelude::*; use vapora_leptos_ui::Pagination; #[component] fn PaginationExample() -> impl IntoView { let (current_page, set_current_page) = signal(1usize); let total_pages = 10; view! {
"Current page: " {move || current_page.get()}
} } ``` ### StatCard ```rust use leptos::prelude::*; use vapora_leptos_ui::StatCard; #[component] fn StatCardExamples() -> impl IntoView { view! {
} } ``` --- ## Forms ### FormField ```rust use leptos::prelude::*; use vapora_leptos_ui::{FormField, Input, Button, Variant}; #[component] fn FormFieldExample() -> impl IntoView { let (username, set_username) = signal(String::new()); let (email, set_email) = signal(String::new()); let (username_error, set_username_error) = signal::>(None); let handle_submit = move |ev: leptos::ev::SubmitEvent| { ev.prevent_default(); // Validate if username.get().trim().is_empty() { set_username_error.set(Some("Username is required".to_string())); return; } // Submit form // ... }; view! {
// Field with error {move || { if let Some(err) = username_error.get() { view! { }.into_any() } else { view! { }.into_any() } }} // Field with help text } } ``` ### Validation ```rust use leptos::prelude::*; use vapora_leptos_ui::{ validate_required, validate_email, validate_min_length, validate_max_length, FormField, Input, Button, Variant }; #[component] fn ValidationExample() -> impl IntoView { let (email, set_email) = signal(String::new()); let (password, set_password) = signal(String::new()); let (email_error, set_email_error) = signal::>(None); let (password_error, set_password_error) = signal::>(None); let validate_form = move || -> bool { let mut valid = true; // Validate email if let Err(err) = validate_required(&email.get(), "Email") { set_email_error.set(Some(err)); valid = false; } else if let Err(err) = validate_email(&email.get()) { set_email_error.set(Some(err)); valid = false; } else { set_email_error.set(None); } // Validate password if let Err(err) = validate_required(&password.get(), "Password") { set_password_error.set(Some(err)); valid = false; } else if let Err(err) = validate_min_length(&password.get(), 8, "Password") { set_password_error.set(Some(err)); valid = false; } else if let Err(err) = validate_max_length(&password.get(), 100, "Password") { set_password_error.set(Some(err)); valid = false; } else { set_password_error.set(None); } valid }; view! {
// Email field with validation {move || { if let Some(err) = email_error.get() { view! { }.into_any() } else { view! { }.into_any() } }} // Password field with validation {move || { if let Some(err) = password_error.get() { view! { }.into_any() } else { view! { }.into_any() } }} } } ``` --- ## Feedback ### Toast ```rust use leptos::prelude::*; use vapora_leptos_ui::{ToastProvider, use_toast, ToastType, Button, Variant}; #[component] fn ToastExample() -> impl IntoView { view! { } } #[component] fn ToastButtons() -> impl IntoView { let toast = use_toast(); view! {
} } ``` --- ## Navigation ### SpaLink ```rust use leptos::prelude::*; use vapora_leptos_ui::SpaLink; #[component] fn SpaLinkExamples() -> impl IntoView { view! {
// Internal link (SPA navigation) "View Projects" // External link (opens in new tab) "GitHub" // Mailto link (external) "Email Support"
} } ``` --- ## Patterns ### Modal with Form Complete example of a modal containing a validated form: ```rust use leptos::prelude::*; use vapora_leptos_ui::{ Modal, FormField, Input, Button, Variant, use_toast, ToastType, validate_required }; #[component] fn CreateProjectModal() -> impl IntoView { let (show_modal, set_show_modal) = signal(false); let (title, set_title) = signal(String::new()); let (description, set_description) = signal(String::new()); let (error, set_error) = signal::>(None); let toast = use_toast(); let handle_submit = move |ev: leptos::ev::SubmitEvent| { ev.prevent_default(); // Validate if let Err(err) = validate_required(&title.get(), "Project title") { set_error.set(Some(err)); return; } // Submit (in real app, call API) toast.show_toast( format!("Created project: {}", title.get()), ToastType::Success ); // Reset and close set_title.set(String::new()); set_description.set(String::new()); set_error.set(None); set_show_modal.set(false); }; view! {

"Create New Project"

// Title field with validation {move || { if let Some(err) = error.get() { view! { }.into_any() } else { view! { }.into_any() } }} // Description field (optional) // Action buttons
} } ``` ### Table with Sorting and Pagination Complete example of a data table with sorting and pagination: ```rust use leptos::prelude::*; use vapora_leptos_ui::{Table, TableColumn, Pagination, Spinner}; const ITEMS_PER_PAGE: usize = 10; #[derive(Clone, Debug)] struct User { id: String, name: String, email: String, status: String, role: String, } #[component] fn UsersTable() -> impl IntoView { let (users, set_users) = signal(Vec::::new()); let (loading, set_loading) = signal(true); let (current_page, set_current_page) = signal(1usize); // Fetch users (simulated) Effect::new(move |_| { spawn_local(async move { // Simulate API call tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; let mock_users = vec![ User { id: "1".to_string(), name: "Alice Smith".to_string(), email: "alice@example.com".to_string(), status: "Active".to_string(), role: "Admin".to_string(), }, // ... more users ]; set_users.set(mock_users); set_loading.set(false); }); }); view! {
"Loading users..."
} > {move || { let all_users = users.get(); let total_items = all_users.len(); let total_pages = total_items.div_ceil(ITEMS_PER_PAGE); // Paginate let page = current_page.get(); let start_idx = (page - 1) * ITEMS_PER_PAGE; let end_idx = (start_idx + ITEMS_PER_PAGE).min(total_items); // Convert to table rows let columns = vec![ TableColumn::new("Name", "name").sortable(), TableColumn::new("Email", "email").sortable(), TableColumn::new("Status", "status").sortable(), TableColumn::new("Role", "role"), ]; let rows: Vec> = all_users .into_iter() .skip(start_idx) .take(end_idx - start_idx) .map(|user| { vec![user.name, user.email, user.status, user.role] }) .collect(); view! {
{move || { if total_pages > 1 { view! {
}.into_any() } else { view! {
}.into_any() } }}
} }} } } ``` ### Dashboard with Stats Complete dashboard example with stat cards and data display: ```rust use leptos::prelude::*; use vapora_leptos_ui::{StatCard, Card, Badge, GlowColor, Spinner}; #[component] fn Dashboard() -> impl IntoView { let (loading, set_loading) = signal(true); let (total_users, set_total_users) = signal(0); let (active_projects, set_active_projects) = signal(0); let (revenue, set_revenue) = signal(0); // Fetch stats Effect::new(move |_| { spawn_local(async move { // Simulate API call tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; set_total_users.set(1234); set_active_projects.set(42); set_revenue.set(12345); set_loading.set(false); }); }); view! {

"Dashboard"

} > // Stats cards
// Recent projects

"Recent Projects"

"Project Alpha"

"Active"

"Multi-agent development platform"

"Project Beta"

"In Progress"

"AI-powered code reviews"

"Project Gamma"

"Completed"

"Automated testing suite"

} } ``` --- ## Best Practices ### Event Handling Always use `Callback::new()` for event handlers: ```rust // ✅ CORRECT