Button

Rust/UI component that displays a button or a component that looks like a button.

button
use leptos::prelude::*;

use crate::components::ui::button::Button;

#[component]
pub fn DemoButton() -> impl IntoView {
    view! { <Button>"Button"</Button> }
}

Installation

You can run either of the following commands:

# cargo install ui-cli --force
ui add demo_button
ui add button

Update the imports to match your project setup.

Copy and paste the following code into your project:

components/ui/button.rs

use leptos::prelude::*;
use leptos_ui::variants;

// TODO 💪 Loading state (demo_use_timeout_fn.rs and demo_button.rs)

variants! {
    Button {
        base: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive  w-fit  hover:cursor-pointer", // Using hover:cursor-pointer as workaround for href_support.
        variants: {
            variant: {
                Default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
                Destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
                Outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/5",
                Secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
                Ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
                Accent: "bg-accent text-accent-foreground hover:bg-accent/80",
                Link: "text-primary underline-offset-4 hover:underline",
                //
                Warning: "bg-warning text-warning-foreground hover:bg-warning/90",
                Success: "bg-success text-success-foreground hover:bg-success/90",
                Bordered: "bg-transparent border border-zinc-200 text-muted-foreground",
            },
            size: {
                Default: "h-9 px-4 py-2 has-[>svg]:px-3",
                Sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
                Lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
                Icon: "size-9",
                //
                Mobile: "px-6 py-3 rounded-[24px]",
                Badge: "px-2.5 py-0.5 text-xs"
            }
        },
        component: {
            element: button,
            support_href: true,
            support_aria_current: true
        }
    }
}

Update the imports to match your project setup.

Usage

use crate::components::ui::button::Button;
<Button>"Click me"</Button>

Examples

Reactive Button

Button component that updates dynamically using Leptos signals to respond to state changes. This example demonstrates how to build reactive UI components in Rust that automatically re-render when underlying data changes.

use leptos::prelude::*;

use crate::components::ui::button::Button;

#[component]
pub fn DemoButtonReactive() -> impl IntoView {
    let count = RwSignal::new(0);
    let increment = move |_| *count.write() += 1;

    view! { <Button on:click=increment>"Click Me: " {count}</Button> }
}

Variants

Available Button style variants include default, destructive, outline, secondary, ghost, and link. Each variant provides different visual styling while maintaining consistent behavior and accessibility standards across your Leptos application.

use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonVariant};

#[component]
pub fn DemoButtonVariants() -> impl IntoView {
    view! {
        <div class="flex flex-col gap-4 items-center @md:flex-row">
            <Button>Default</Button>

            <Button variant=ButtonVariant::Secondary>Secondary</Button>
            <Button variant=ButtonVariant::Outline>Outline</Button>
            <Button variant=ButtonVariant::Ghost>Ghost</Button>
            <Button variant=ButtonVariant::Destructive>Destructive</Button>
        </div>
    }
}

Sizes

Button size options include small, default, large, and icon sizes. This example shows how to implement responsive button sizing in your Rust UI components to match different design requirements and use cases.

use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonSize};

#[component]
pub fn DemoButtonSizes() -> impl IntoView {
    view! {
        <div class="flex gap-4">
            <Button size=ButtonSize::Sm>Small</Button>
            <Button>Default</Button>
            <Button size=ButtonSize::Lg>Large</Button>
        </div>
    }
}

Disabled

Disabled Button state with proper ARIA attributes for accessibility. This example demonstrates the correct way to implement non-interactive button states while maintaining semantic HTML and screen reader compatibility.

use leptos::prelude::*;

use crate::components::ui::button::Button;

#[component]
pub fn DemoButtonDisabled() -> impl IntoView {
    view! {
        <div class="flex gap-4">
            <Button>"Normal"</Button>
            <Button attr:disabled=true>"Disabled"</Button>
        </div>
    }
}

Overriding Button

Customize Button styles and behavior by overriding default properties with custom classes. This example shows how to extend the base button component while preserving type safety and component composition patterns in Leptos.

use leptos::prelude::*;

use crate::components::ui::button::Button;

#[component]
pub fn DemoButtonOverride() -> impl IntoView {
    view! { <Button class="hover:bg-pink-500 bg-sky-500">Fancy Button</Button> }
}

Button with Clx

Use the Clx utility for conditional styling and dynamic class composition in Leptos components. This example demonstrates how to apply conditional Tailwind CSS classes based on component state or props for flexible styling.

use leptos::prelude::*;
use leptos_ui::clx;

// * 💁 Define your reusable Component here:
clx! {MyButton, button, "px-4 py-2 bg-neutral-900 text-white rounded-md"}

/* ========================================================== */
/*                     ✨ FUNCTIONS ✨                        */
/* ========================================================== */

#[component]
pub fn DemoButtonWithClx() -> impl IntoView {
    let count = RwSignal::new(0);
    let on_click = move |_| *count.write() += 1;

    view! {
        <MyButton class="bg-sky-500" on:click=on_click>
            "Click Me: "
            {count}
        </MyButton>
    }
}

With Href

Automatic conversion to semantic a tag with data-slot attribute when using the href prop. This example shows how the button component intelligently switches between button and anchor elements for proper HTML semantics and SEO.

use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonVariant};

#[component]
pub fn DemoButtonHref() -> impl IntoView {
    view! {
        <div class="flex gap-10 p-4 rounded-lg border">
            <Button href="/dashboard">"Go to Dashboard"</Button>
            <Button href="/dashboard" variant=ButtonVariant::Ghost class="border-2 border-dashed">
                "Go to Dashboard (custom)"
            </Button>
            <Button href="https://ever-ui.com/" attr:rel="noopener" attr:target="_blank">
                "External Link"
            </Button>
        </div>
    }
}

Stateful

Button component that manages its own internal state using Leptos signals. This example demonstrates building self-contained interactive components with local state management, perfect for toggles, loading states, and complex user interactions.

use leptos::prelude::*;

use crate::components::ui::button::Button;

#[component]
pub fn DemoButtonStateful() -> impl IntoView {
    view! {
        <style>
            {r#"
            @media (prefers-reduced-motion: no-preference) {
            ::view-transition-old(button-stateful),
            ::view-transition-new(button-stateful) {
            height: 100%;
            width: 100%;
            }
            }
            "#}
        </style>

        <Button attr:id="ButtonStateful" attr:style="view-transition-name: button-stateful;">
            Do some hard work
        </Button>

        <script>
            {r#"
            (() => {
            const STATES = {
            idle: "Do some hard work",
            working: "⏳ working...",
            done: "Done! ✅",
            };
            
            ButtonStateful.onclick = () => {
            setState("working");
            setTimeout(() => setState("done"), 2000);
            setTimeout(() => setState("idle"), 4000);
            };
            
            function setState(state) {
            if (!document.startViewTransition) ButtonStateful.innerHTML = STATES[state];
            else document.startViewTransition(() => (ButtonStateful.innerHTML = STATES[state]));
            }
            })();
            "#}
        </script>
    }
}

Get notified when new stuff drops.

Rust/UI Icons - Send