Forms

Select

A dropdown select with icons, avatars, grouped items, descriptions, and custom rendering. Built on bits-ui with full keyboard navigation.

Playground

Experiment with different props in real-time.

Basic Usage

Pass items and use bind:value.

<script lang="ts">
  import { Select } from 'sv5ui';

  let value = $state('');

  const countries = [
    { value: 'us', label: 'United States' },
    { value: 'ca', label: 'Canada' },
    { value: 'mx', label: 'Mexico' },
    { value: 'br', label: 'Brazil' },
    { value: 'ar', label: 'Argentina' }
  ];
</script>

<Select items={countries} bind:value placeholder="Select a country" />

Variants

5 trigger visual styles.

<Select items={frameworks} variant="outline" placeholder="Outline" />
<Select items={frameworks} variant="none" placeholder="None" />
<Select items={frameworks} variant="soft" placeholder="Soft" />
<Select items={frameworks} variant="subtle" placeholder="Subtle" />
<Select items={frameworks} variant="ghost" placeholder="Ghost" />

Sizes

5 sizes.

<Select items={frameworks} size="xs" placeholder="Extra Small" />
<Select items={frameworks} size="sm" placeholder="Small" />
<Select items={frameworks} size="md" placeholder="Medium" />
<Select items={frameworks} size="lg" placeholder="Large" />
<Select items={frameworks} size="xl" placeholder="Extra Large" />

Colors

Color schemes visible with highlight.

<Select items={frameworks} color="primary" placeholder="Primary" />
<Select items={frameworks} color="secondary" placeholder="Secondary" />
<Select items={frameworks} color="success" placeholder="Success" />
<Select items={frameworks} color="warning" placeholder="Warning" />
<Select items={frameworks} color="error" placeholder="Error" />
<Select items={frameworks} color="info" placeholder="Info" />

With Icons

Add icons to items and the trigger.

<script lang="ts">
  const browsers = [
    { value: 'chrome', label: 'Chrome', icon: 'logos:chrome' },
    { value: 'firefox', label: 'Firefox', icon: 'logos:firefox' },
    { value: 'safari', label: 'Safari', icon: 'logos:safari' },
    { value: 'edge', label: 'Edge', icon: 'logos:microsoft-edge' }
  ];
</script>

<Select items={browsers} placeholder="Choose a browser" icon="lucide:globe" />

With Descriptions

Add secondary text to items.

<script lang="ts">
  const roles = [
    { value: 'admin', label: 'Admin', description: 'Full access to all resources' },
    { value: 'editor', label: 'Editor', description: 'Can edit and publish content' },
    { value: 'viewer', label: 'Viewer', description: 'Read-only access' }
  ];
</script>

<Select items={roles} placeholder="Assign a role" />

Grouped Items

Use type: 'label' and type: 'separator'.

<script lang="ts">
  const items = [
    { type: 'label', label: 'Frontend' },
    { value: 'react', label: 'React' },
    { value: 'vue', label: 'Vue' },
    { value: 'svelte', label: 'Svelte' },
    { type: 'separator' },
    { type: 'label', label: 'Backend' },
    { value: 'node', label: 'Node.js' },
    { value: 'python', label: 'Python' },
    { value: 'go', label: 'Go' }
  ];
</script>

<Select items={items} placeholder="Pick a technology" />

With Avatar

Display avatars alongside items.

<script lang="ts">
  const members = [
    { value: 'alice', label: 'Alice Martin', avatar: { src: 'https://i.pravatar.cc/40?u=alice', alt: 'Alice' } },
    { value: 'bob', label: 'Bob Wilson', avatar: { src: 'https://i.pravatar.cc/40?u=bob', alt: 'Bob' } },
    { value: 'carol', label: 'Carol Lee', avatar: { src: 'https://i.pravatar.cc/40?u=carol', alt: 'Carol' } }
  ];
</script>

<Select items={members} placeholder="Assign to member" />

Disabled

Disable the select or individual items.

<script lang="ts">
  const plans = [
    { value: 'free', label: 'Free' },
    { value: 'pro', label: 'Pro' },
    { value: 'enterprise', label: 'Enterprise', disabled: true }
  ];
</script>

<Select items={plans} placeholder="Select a plan" />
<Select items={plans} placeholder="Disabled select" disabled />

Multiple Selection

Set multiple to pick more than one option. value becomes a string[], the dropdown stays open after each pick, and the trigger joins labels with separator (default ", ").

<script lang="ts">
  import { Select } from 'sv5ui';

  let value = $state<string[]>(['us', 'ca']);
  // Initialize the value via state instead of a defaultValue prop
  let preset = $state<string[]>(['mx', 'br']);

  const countries = [
    { value: 'us', label: 'United States' },
    { value: 'ca', label: 'Canada' },
    { value: 'mx', label: 'Mexico' },
    { value: 'br', label: 'Brazil' },
    { value: 'ar', label: 'Argentina' }
  ];
</script>

<!-- Default separator: ", " -->
<Select items={countries} bind:value multiple placeholder="Pick countries" />

<!-- Custom separator -->
<Select items={countries} bind:value={preset} multiple separator=" • " placeholder="Pick countries" />

Multiple with Chips

Render selected values as chips with the selected snippet. It receives { items, remove, clear } - call stopPropagation on remove clicks so the trigger does not toggle.

<script lang="ts">
  import { Select, Badge } from 'sv5ui';

  let value = $state<string[]>(['react', 'svelte']);

  const items = [
    { type: 'label', label: 'Frontend' },
    { value: 'react', label: 'React' },
    { value: 'vue', label: 'Vue' },
    { value: 'svelte', label: 'Svelte' },
    { type: 'separator' },
    { type: 'label', label: 'Backend' },
    { value: 'node', label: 'Node.js' },
    { value: 'python', label: 'Python' },
    { value: 'go', label: 'Go' }
  ];
</script>

<Select {items} bind:value multiple placeholder="Pick technologies">
  {#snippet selected({ items, remove })}
    <div class="flex flex-wrap items-center gap-1">
      {#each items as item (item.value)}
        <Badge
          label={item.label}
          variant="soft"
          size="sm"
          trailingIcon="lucide:x"
          class="cursor-pointer"
          onclick={(e) => { e.stopPropagation(); remove(item.value); }}
        />
      {/each}
    </div>
  {/snippet}
</Select>

UI Slots

Use the ui prop to override classes on internal elements.

SlotDescription
rootOutermost container wrapper
baseTrigger button element
leadingLeading content wrapper
leadingIconLeading icon element
leadingAvatarLeading avatar element
trailingTrailing content wrapper
trailingIconTrailing icon (chevron)
valueSelected value text
placeholderPlaceholder text
contentDropdown content panel
viewportScrollable viewport
groupOption group wrapper
groupLabelGroup label text
itemIndividual option
itemIconIcon in option
itemAvatarAvatar in option
itemLabelLabel text in option
itemDescriptionDescription in option
itemIndicatorSelected checkmark
separatorSeparator between groups
<Select
  items={countries}
  placeholder="Custom styled"
  ui={{
    base: 'rounded-full shadow-lg',
    content: 'rounded-xl',
    item: 'rounded-lg'
  }}
/>

Snippets

SnippetDescription
leadingSlotCustom content before selected value
trailingSlotCustom content after selected value
selectedCustom rendering of selected value(s) in the trigger (multiple mode). Receives { items, remove, clear }
itemCustom option rendering (receives item, index, selected)
itemLeadingCustom option leading (receives item, index, selected)
itemLabelCustom option label (receives item, index, selected)
itemTrailingCustom option trailing (receives item, index, selected)
contentCustom dropdown content (receives open)

Props

PropTypeDefault
itemsSelectItemType[][]
valuestring | string[]-
multiplebooleanfalse
separatorstring', '
openbooleanfalse
placeholderstring-
variant'outline' | 'soft' | 'subtle' | 'ghost' | 'none''outline'
colorColorType'primary'
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
highlightbooleanfalse
loadingbooleanfalse
disabledbooleanfalse
iconstring-
leadingIconstring-
trailingIconstring'lucide:chevron-down'
selectedIconstring'lucide:check'
avatarAvatarProps-
requiredbooleanfalse
namestring-
refHTMLElement | nullnull
classstring-
uiRecord<Slot, Class>-

Item Props

Use type: 'label' for group headings and type: 'separator' for dividers.

PropTypeDefault
valuestring-
labelstring-
descriptionstring-
iconstring-
avatarAvatarProps-
disabledbooleanfalse