# Interactive Tutorial Guide - New York Theme Components ## 🎯 Overview This comprehensive tutorial guide will walk you through building interactive applications using the New York theme variants of our Leptos shadcn/ui components. You'll learn how to create engaging user interfaces with proper state management, form validation, and component interactions. ## 📚 Table of Contents 1. [Getting Started](#getting-started) 2. [Component Basics](#component-basics) 3. [State Management](#state-management) 4. [Form Handling](#form-handling) 5. [Interactive Features](#interactive-features) 6. [Advanced Patterns](#advanced-patterns) 7. [Best Practices](#best-practices) 8. [Troubleshooting](#troubleshooting) ## 🚀 Getting Started ### Prerequisites Before starting this tutorial, make sure you have: - Rust 1.70+ installed - Leptos 0.8+ framework - Basic understanding of Rust and Leptos - Familiarity with HTML/CSS concepts ### Project Setup 1. **Create a new Leptos project:** ```bash cargo new my-leptos-app --bin cd my-leptos-app ``` 2. **Add dependencies to Cargo.toml:** ```toml [dependencies] leptos = "0.8" leptos-shadcn-button = "0.8" leptos-shadcn-card = "0.8" leptos-shadcn-input = "0.8" leptos-shadcn-form = "0.8" ``` 3. **Import New York theme components:** ```rust use leptos_shadcn_button::new_york::{Button as ButtonNewYork, ButtonVariant as ButtonVariantNewYork}; use leptos_shadcn_card::new_york::{Card as CardNewYork, CardHeader as CardHeaderNewYork}; use leptos_shadcn_input::new_york::Input as InputNewYork; ``` ## 🧩 Component Basics ### Button Components The New York theme provides several button variants with consistent styling: ```rust #[component] pub fn ButtonShowcase() -> impl IntoView { view! {
// Default button "Default Button" // Destructive button "Delete Item" // Outline button "Cancel" // Secondary button "Secondary Action" // Ghost button "Ghost Button" // Link button "Learn More"
} } ``` ### Card Components Cards provide structured content containers: ```rust #[component] pub fn CardShowcase() -> impl IntoView { view! { "Card Title" "This is a description of the card content."

"This is the main content of the card."

"Action"
} } ``` ### Input Components Input components handle user data entry: ```rust #[component] pub fn InputShowcase() -> impl IntoView { let (value, set_value) = signal("".to_string()); view! {
} } ``` ## 🔄 State Management ### Basic State with Signals Leptos uses signals for reactive state management: ```rust #[component] pub fn StateManagementExample() -> impl IntoView { // Create reactive signals let (count, set_count) = signal(0); let (name, set_name) = signal("".to_string()); let (is_visible, set_is_visible) = signal(true); // Derived state let double_count = Signal::derive(move || count.get() * 2); view! {
// Counter example

"Counter: " {count}

"Double: " {double_count}

"Increment" "Decrement"
// Name input

"Hello, " {move || if name.get().is_empty() { "Anonymous".to_string() } else { name.get() }} "!"

// Visibility toggle
{move || if is_visible.get() { "Hide" } else { "Show" }} " Content" {move || if is_visible.get() { view! {
"This content is visible!"
} } else { view! {
} }}
} } ``` ### Complex State Management For more complex applications, use structured state: ```rust #[derive(Clone, Default)] struct AppState { user: Option, notifications: Vec, theme: String, loading: bool, } #[derive(Clone)] struct User { name: String, email: String, preferences: UserPreferences, } #[derive(Clone)] struct UserPreferences { theme: String, notifications_enabled: bool, } #[component] pub fn ComplexStateExample() -> impl IntoView { let (app_state, set_app_state) = signal(AppState::default()); // State update functions let update_user = move |user: User| { set_app_state.update(|state| state.user = Some(user)); }; let add_notification = move |notification: Notification| { set_app_state.update(|state| { state.notifications.push(notification); if state.notifications.len() > 10 { state.notifications.remove(0); } }); }; let set_loading = move |loading: bool| { set_app_state.update(|state| state.loading = loading); }; view! {
// User info display {move || { if let Some(user) = app_state.get().user.clone() { view! { "User Profile"

"Name: " {user.name}

"Email: " {user.email}

"Theme: " {user.preferences.theme}

} } else { view! {

"No user logged in"

} } }} // Loading state {move || if app_state.get().loading { view! {
"Loading..."
} } else { view! {
} }}
} } ``` ## 📝 Form Handling ### Basic Form with Validation ```rust #[derive(Clone, Default)] struct ContactForm { name: String, email: String, message: String, } #[derive(Clone)] struct FormErrors { name: Option, email: Option, message: Option, } impl Default for FormErrors { fn default() -> Self { Self { name: None, email: None, message: None, } } } #[component] pub fn ContactFormExample() -> impl IntoView { let (form_data, set_form_data) = signal(ContactForm::default()); let (errors, set_errors) = signal(FormErrors::default()); let (is_submitting, set_is_submitting) = signal(false); let (is_submitted, set_is_submitted) = signal(false); // Validation function let validate_form = move || { let mut new_errors = FormErrors::default(); let data = form_data.get(); if data.name.trim().is_empty() { new_errors.name = Some("Name is required".to_string()); } if data.email.trim().is_empty() { new_errors.email = Some("Email is required".to_string()); } else if !data.email.contains('@') { new_errors.email = Some("Please enter a valid email".to_string()); } if data.message.trim().is_empty() { new_errors.message = Some("Message is required".to_string()); } set_errors.set(new_errors.clone()); new_errors.name.is_none() && new_errors.email.is_none() && new_errors.message.is_none() }; // Form submission let handle_submit = move |_| { if validate_form() { set_is_submitting.set(true); // Simulate API call set_timeout(move || { set_is_submitting.set(false); set_is_submitted.set(true); set_form_data.set(ContactForm::default()); }, 2000); } }; view! { "Contact Us" "Send us a message and we'll get back to you soon." {move || if is_submitted.get() { view! {
"✓"

"Message Sent!"

"Thank you for your message. We'll get back to you soon."

"Send Another Message"
} } else { view! {
// Name field
{move || if let Some(error) = errors.get().name.clone() { view! {

{error}

} } else { view! {
} }}
// Email field
{move || if let Some(error) = errors.get().email.clone() { view! {

{error}

} } else { view! {
} }}
// Message field