Item

A flexible container component for displaying list items with media, content, and actions.

Basic Item

A simple item with title and description.

Rust/UI Icons - BadgeCheck
Your profile has been verified.
Rust/UI Icons - ChevronRight
use icons::{BadgeCheck, ChevronRight};
use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonSize, ButtonVariant};
use crate::components::ui::item::{
    Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemSize, ItemTitle, ItemVariant,
};

#[component]
pub fn DemoItem() -> impl IntoView {
    view! {
        <div class="flex flex-col gap-6 w-full max-w-md">
            <Item variant=ItemVariant::Outline>
                <ItemContent>
                    <ItemTitle>"Basic Item"</ItemTitle>
                    <ItemDescription>"A simple item with title and description."</ItemDescription>
                </ItemContent>
                <ItemActions>
                    <Button variant=ButtonVariant::Outline size=ButtonSize::Sm>
                        "Action"
                    </Button>
                </ItemActions>
            </Item>

            <Item variant=ItemVariant::Outline size=ItemSize::Sm href="#">
                <ItemMedia>
                    <BadgeCheck class="size-5" />
                </ItemMedia>
                <ItemContent>
                    <ItemTitle>"Your profile has been verified."</ItemTitle>
                </ItemContent>
                <ItemActions>
                    <ChevronRight class="size-4" />
                </ItemActions>
            </Item>
        </div>
    }
}

Installation

You can run either of the following commands:

# cargo install ui-cli --force
ui add demo_item
ui add item

Update the imports to match your project setup.

Copy and paste the following code into your project:

components/ui/item.rs

use leptos::prelude::*;
use leptos_ui::{clx, variants};
use tw_merge::tw_merge;

use crate::registry::ui::separator::Separator;

mod components {
    use super::*;
    clx! {ItemGroup, div, "group/item-group flex flex-col"}
    clx! {ItemContent, div, "flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none"}
    clx! {ItemTitle, div, "flex w-fit items-center gap-2 text-sm leading-snug font-medium"}
    clx! {ItemDescription, p, "text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4"}
    clx! {ItemActions, div, "flex items-center gap-2"}
    clx! {ItemHeader, div, "flex basis-full items-center justify-between gap-2"}
    clx! {ItemFooter, div, "flex basis-full items-center justify-between gap-2"}
}

pub use components::*;

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

variants! {
    Item {
        base: "group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
        variants: {
            variant: {
                Default: "bg-transparent",
                Outline: "border-border",
                Muted: "bg-muted/50",
            },
            size: {
                Default: "p-4 gap-4",
                Sm: "py-3 px-4 gap-2.5",
            }
        },
        component: {
            element: div,
            support_href: true
        }
    }
}

variants! {
    ItemMedia {
        base: "flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5",
        variants: {
            variant: {
                Default: "bg-transparent",
                Icon: "size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4",
                Image: "size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover",
            },
            size: {
                Default: "",
            }
        },
        component: {
            element: div
        }
    }
}

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

#[component]
pub fn ItemSeparator(#[prop(into, optional)] class: String) -> impl IntoView {
    let merged_class = tw_merge!("my-0", class);

    view! { <Separator attr:data-name="ItemSeparator" class=merged_class /> }
}

Update the imports to match your project setup.

Usage

You can use the Item component in combination with the Button, Avatar and DropdownMenu components.

use crate::components::ui::item::{Item, ItemContent, ItemTitle, ItemDescription, ItemActions, ItemMedia};
<Item variant=ItemVariant::Outline>
    <ItemContent>
        <ItemTitle>"Item Title"</ItemTitle>
        <ItemDescription>"Item description text."</ItemDescription>
    </ItemContent>
</Item>

Examples

Variants

Item component with multiple visual style variants including default, outline, and muted options. This example demonstrates how to create consistent list item styling in Leptos with different visual emphasis levels for building flexible UI layouts in Rust applications.

Default Variant

Standard styling with subtle background and borders.

Outline Variant

Outlined style with clear borders and transparent background.

Muted Variant

Subdued appearance with muted colors for secondary content.

use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonSize, ButtonVariant};
use crate::components::ui::item::{Item, ItemActions, ItemContent, ItemDescription, ItemTitle, ItemVariant};

#[component]
pub fn DemoItemVariants() -> impl IntoView {
    view! {
        <div class="flex flex-col gap-6">
            <Item>
                <ItemContent>
                    <ItemTitle>"Default Variant"</ItemTitle>
                    <ItemDescription>"Standard styling with subtle background and borders."</ItemDescription>
                </ItemContent>
                <ItemActions>
                    <Button variant=ButtonVariant::Outline size=ButtonSize::Sm>
                        "Open"
                    </Button>
                </ItemActions>
            </Item>

            <Item variant=ItemVariant::Outline>
                <ItemContent>
                    <ItemTitle>"Outline Variant"</ItemTitle>
                    <ItemDescription>"Outlined style with clear borders and transparent background."</ItemDescription>
                </ItemContent>
                <ItemActions>
                    <Button variant=ButtonVariant::Outline size=ButtonSize::Sm>
                        "Open"
                    </Button>
                </ItemActions>
            </Item>

            <Item variant=ItemVariant::Muted>
                <ItemContent>
                    <ItemTitle>"Muted Variant"</ItemTitle>
                    <ItemDescription>"Subdued appearance with muted colors for secondary content."</ItemDescription>
                </ItemContent>
                <ItemActions>
                    <Button variant=ButtonVariant::Outline size=ButtonSize::Sm>
                        "Open"
                    </Button>
                </ItemActions>
            </Item>
        </div>
    }
}

Item component integration with dropdown menus for rich content selection interfaces. This example shows how to combine Item and DropdownMenu components in Leptos to build sophisticated selection menus with Avatar media, descriptions, and actions in Rust applications.

use leptos::prelude::*;

use crate::components::ui::avatar::{Avatar, AvatarFallback};
use crate::components::ui::dropdown_menu::{
    DropdownMenu, DropdownMenuAlign, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
};
use crate::components::ui::item::{Item, ItemContent, ItemDescription, ItemMedia, ItemSize, ItemTitle};

#[component]
pub fn DemoItemDropdownMenu() -> impl IntoView {
    view! {
        <DropdownMenu align=DropdownMenuAlign::End>
            <DropdownMenuTrigger class="w-fit">"Dropdown"</DropdownMenuTrigger>

            <DropdownMenuContent class="w-72 [--radius:0.65rem]">
                {PEOPLE
                    .iter()
                    .map(|person| {
                        view! {
                            <DropdownMenuItem class="p-0">
                                <Item size=ItemSize::Sm class="p-2 w-full">
                                    <ItemMedia>
                                        <Avatar class="size-8">
                                            <AvatarFallback>{person.initials}</AvatarFallback>
                                        </Avatar>
                                    </ItemMedia>
                                    <ItemContent class="gap-0.5">
                                        <ItemTitle>{person.username}</ItemTitle>
                                        <ItemDescription>{person.email}</ItemDescription>
                                    </ItemContent>
                                </Item>
                            </DropdownMenuItem>
                        }
                    })
                    .collect::<Vec<_>>()}
            </DropdownMenuContent>
        </DropdownMenu>
    }
}

/* ========================================================== */
/*                     ✨ CONSTANTS ✨                        */
/* ========================================================== */

#[derive(Clone)]
struct Person {
    username: &'static str,
    initials: &'static str,
    email: &'static str,
}

const PEOPLE: &[Person] = &[
    Person { username: "Ryan Smith", initials: "RS", email: "[email protected]" },
    Person { username: "Morgan Williams", initials: "MW", email: "[email protected]" },
    Person { username: "Max Murphy", initials: "MM", email: "[email protected]" },
];

Get notified when new stuff drops.

Rust/UI Icons - Send