Skip to content

UI Primitives

All UI primitives live in $lib/components/ui/ and are built on top of bits-ui with styling from tailwind-variants. Each component directory contains a barrel index.ts for clean imports.

The most commonly used component. Supports rendering as a <button> or <a> depending on whether href is provided.

Import: $lib/components/ui/button

Variants: default | outline | secondary | ghost | destructive | link | glass | glass-outline

Sizes: default | xs | sm | lg | icon | icon-xs | icon-sm | icon-lg

<script lang="ts">
import { Button } from "$lib/components/ui/button";
</script>
<!-- Standard button -->
<Button variant="default">Save</Button>
<!-- Link styled as a button -->
<Button href="/settings" variant="outline">Settings</Button>
<!-- Icon button -->
<Button variant="ghost" size="icon" aria-label="Search">
<SearchIcon />
</Button>
<!-- Destructive action -->
<Button variant="destructive">Delete Account</Button>

A single <input> component that handles both standard inputs and file inputs.

Import: $lib/components/ui/input

<script lang="ts">
import { Input } from "$lib/components/ui/input";
let name = $state("");
let files = $state<FileList>();
</script>
<!-- Text input -->
<Input type="text" placeholder="Full name" bind:value={name} />
<!-- File input with bound FileList -->
<Input type="file" bind:files />

Accessible dropdown select built on bits-ui. Composed of multiple sub-components.

Import: $lib/components/ui/select

<script lang="ts">
import * as Select from "$lib/components/ui/select";
</script>
<Select.Root>
<Select.Trigger>
<Select.Value placeholder="Choose a role" />
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.GroupHeading>Roles</Select.GroupHeading>
<Select.Item value="admin">Admin</Select.Item>
<Select.Item value="editor">Editor</Select.Item>
<Select.Item value="viewer">Viewer</Select.Item>
</Select.Group>
</Select.Content>
</Select.Root>

Modal dialog with overlay, header, footer, and close button.

Import: $lib/components/ui/dialog

<script lang="ts">
import * as Dialog from "$lib/components/ui/dialog";
import { Button } from "$lib/components/ui/button";
</script>
<Dialog.Root>
<Dialog.Trigger>
{#snippet child({ props })}
<Button {...props}>Open Dialog</Button>
{/snippet}
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Confirm Action</Dialog.Title>
<Dialog.Description>This action cannot be undone.</Dialog.Description>
</Dialog.Header>
<p>Are you sure you want to proceed?</p>
<Dialog.Footer>
<Dialog.Close>Cancel</Dialog.Close>
<Button variant="destructive">Confirm</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

Container component for grouping related content.

Import: $lib/components/ui/card

<script lang="ts">
import * as Card from "$lib/components/ui/card";
</script>
<Card.Root>
<Card.Header>
<Card.Title>Monthly Revenue</Card.Title>
<Card.Description>Revenue for the current billing period.</Card.Description>
</Card.Header>
<Card.Content>
<p class="text-3xl font-bold">$12,450</p>
</Card.Content>
<Card.Footer>
<Card.Action>View Details</Card.Action>
</Card.Footer>
</Card.Root>

Inline status indicator with variant support. Renders as <span> by default, or <a> when href is provided.

Import: $lib/components/ui/badge

Variants: default | secondary | destructive | outline

<script lang="ts">
import { Badge } from "$lib/components/ui/badge";
</script>
<Badge>Active</Badge>
<Badge variant="secondary">Draft</Badge>
<Badge variant="destructive">Expired</Badge>
<Badge variant="outline">Preview</Badge>

Tab navigation for switching between content panels.

Import: $lib/components/ui/tabs

<script lang="ts">
import * as Tabs from "$lib/components/ui/tabs";
</script>
<Tabs.Root value="overview">
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="analytics">Analytics</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview">Overview content here.</Tabs.Content>
<Tabs.Content value="analytics">Analytics content here.</Tabs.Content>
<Tabs.Content value="settings">Settings content here.</Tabs.Content>
</Tabs.Root>

Slide-in panel that can appear from any edge of the screen.

Import: $lib/components/ui/sheet

<script lang="ts">
import * as Sheet from "$lib/components/ui/sheet";
import { Button } from "$lib/components/ui/button";
</script>
<Sheet.Root>
<Sheet.Trigger>
{#snippet child({ props })}
<Button variant="outline" {...props}>Open Panel</Button>
{/snippet}
</Sheet.Trigger>
<Sheet.Content side="right">
<Sheet.Header>
<Sheet.Title>Filters</Sheet.Title>
<Sheet.Description>Adjust your search filters.</Sheet.Description>
</Sheet.Header>
<!-- Filter controls here -->
</Sheet.Content>
</Sheet.Root>

Mobile-friendly bottom drawer built on Vaul for touch interactions.

Import: $lib/components/ui/drawer

<script lang="ts">
import * as Drawer from "$lib/components/ui/drawer";
</script>
<Drawer.Root>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Share Post</Drawer.Title>
</Drawer.Header>
<!-- Drawer body -->
<Drawer.Footer>
<Drawer.Close>Done</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>

The following components are also available in $lib/components/ui/:

ComponentPathDescription
Avatarui/avatarUser avatar with fallback initials
Breadcrumbui/breadcrumbNavigation breadcrumb trail
Calendarui/calendarDate picker calendar
Checkboxui/checkboxCheckbox input
Collapsibleui/collapsibleExpandable/collapsible section
Commandui/commandCommand palette / combobox
Dropdown Menuui/dropdown-menuContext and dropdown menus
Fieldui/fieldForm field wrapper with label and error
Input Groupui/input-groupGrouped input with addons
Labelui/labelForm label
Separatorui/separatorHorizontal or vertical divider
Skeletonui/skeletonLoading placeholder
Switchui/switchToggle switch
Tableui/tableBasic HTML table with styling
Textareaui/textareaMulti-line text input
Toggleui/togglePressable toggle button
Toggle Groupui/toggle-groupGroup of toggle buttons
Tooltipui/tooltipHover tooltip

All components follow the same import pattern:

<script lang="ts">
// Single export components
import { Checkbox } from "$lib/components/ui/checkbox";
// Multi-part components (use namespace import)
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
</script>