Kumo Svelte
  • Home
  • Installation
  • Colors
  • Accessibility
  • Changelog
  • Autocomplete
  • Badge
  • Banner
  • Breadcrumbs
  • Button
  • Checkbox
  • Clipboard Text
  • Cloudflare Logo
  • CodeHighlighted
  • Collapsible
  • Combobox
  • Command Palette
  • Date Picker
  • Dialog
  • Dropdown
  • Empty
  • Flow
  • Grid
  • Input
  • InputArea
  • InputGroup
  • Label
  • Layer Card
  • Link
  • Loader
  • MenuBar
  • Meter
  • Pagination
  • Popover
  • Radio
  • Select
  • Sensitive Input
  • Sidebar
  • Skeleton Line
  • Switch
  • Table
  • Table of Contents
  • Tabs
  • Text
  • Toast
  • Toolbar
  • Tooltip
  • Charts
  • Colors
  • Timeseries
  • Maps
  • Sankey
  • Custom Chart
  • Page Header
  • Resource List
  • Delete Resource
Select
kumo-svelte

Select

Displays a list of options for the user to pick from—triggered by a button.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("apple");
</script>

<Select.Root
  label="Favorite Fruit"
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>

Import

import * as Select from "kumo-svelte/components/select";

Usage

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("apple");
</script>

<Select.Root
  label="Favorite Fruit"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>

Examples

Basic

A select with a visible label. When you provide the label prop, the select renders with field labeling.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("apple");
</script>

<Select.Root
  label="Favorite Fruit"
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>

Sizes

Use the size prop to match Input sizing (xs, sm, base, lg).

xs
sm
base
lg
<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const sizes = ["xs", "sm", "base", "lg"] as const;
</script>

<div class="grid gap-4">
  {#each sizes as size}
    <div class="flex items-center gap-3">
      <span class="w-10 text-sm text-kumo-subtle">{size}</span>
      <Select.Root
        aria-label={`Select size ${size}`}
        {size}
        class="w-[200px]"
        placeholder="Choose..."
        items={{ a: "Option A", b: "Option B" }}
      />
    </div>
  {/each}
</div>

Without Visible Label

When a visible label isn’t needed, use aria-label for accessibility.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("apple");
</script>

<Select.Root
  aria-label="Select a fruit"
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>

With Description

Select integrates with Field-style description text below the input.

Choose the category that best describes your issue

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("");
</script>

<Select.Root
  label="Issue Type"
  description="Choose the category that best describes your issue"
  class="w-[280px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ bug: "Bug", documentation: "Documentation", feature: "Feature" }}
/>

With Error

Pass the error prop to display a validation error. When an error is present, it replaces the description in the UI.

Please select an issue type

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
</script>

<Select.Root
  label="Issue Type"
  error="Please select an issue type"
  class="w-[280px]"
  items={{ bug: "Bug", documentation: "Documentation", feature: "Feature" }}
/>

Placeholder

Use the placeholder prop to show text when no value is selected.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("");
</script>

<Select.Root
  label="Category"
  placeholder="Choose a category..."
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ bug: "Bug", documentation: "Documentation", feature: "Feature" }}
/>

Label with Tooltip

Add a tooltip icon next to the label for additional context using labelTooltip.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("");
</script>

{#snippet labelTooltip()}
  Higher priority issues are addressed first
{/snippet}

<Select.Root
  label="Priority"
  {labelTooltip}
  placeholder="Select priority"
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{ low: "Low", medium: "Medium", high: "High", critical: "Critical" }}
/>

Custom Rendering

Use Select.Option children and labels to customize how options appear in the list.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const languages = [
    { value: "en", label: "English", emoji: "🇬🇧" },
    { value: "fr", label: "French", emoji: "🇫🇷" },
    { value: "de", label: "German", emoji: "🇩🇪" },
    { value: "es", label: "Spanish", emoji: "🇪🇸" },
  ];

  let value = $state("en");
</script>

<Select.Root label="Language" class="w-[200px]" {value} onValueChange={(next) => (value = next as string)}>
  {#each languages as language (language.value)}
    <Select.Option value={language.value} label={`${language.emoji} ${language.label}`}>
      {language.emoji} {language.label}
    </Select.Option>
  {/each}
</Select.Root>

Loading

A select component with loading state. The loading state is passed via the loading prop.

Loading State

Loading From Server (simulated delay)

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
</script>

<Select.Root aria-label="Loading select" class="w-[200px]" loading />

Multiple Selection

Enable multiple selection with the multiple prop. The value becomes an array of selected strings.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state<string[]>(["Name", "Location", "Size"]);
</script>

<Select.Root
  label="Visible Columns"
  class="w-[250px]"
  multiple
  {value}
  onValueChange={(next) => (value = next as string[])}
>
  <Select.Option value="Name">Name</Select.Option>
  <Select.Option value="Location">Location</Select.Option>
  <Select.Option value="Size">Size</Select.Option>
  <Select.Option value="Read">Read</Select.Option>
  <Select.Option value="Write">Write</Select.Option>
  <Select.Option value="CreatedAt">Created At</Select.Option>
</Select.Root>

More Example

Select the primary author for this document

<script lang="ts">
  import { Text } from "kumo-svelte/components/text";
  import * as Select from "kumo-svelte/components/select";
  const authors = [
    { id: "1", name: "John Doe", title: "Programmer" },
    { id: "2", name: "Alice Smith", title: "Software Engineer" },
    { id: "3", name: "Michael Chan", title: "UI/UX Designer" },
    { id: "4", name: "Sok Dara", title: "DevOps Engineer" },
  ];

  let value = $state("");
</script>

<Select.Root
  label="Author"
  description="Select the primary author for this document"
  placeholder="Select an author"
  class="w-[220px]"
  {value}
  onValueChange={(next) => (value = next as string)}
>
  {#each authors as author (author.id)}
    <Select.Option value={author.id} label={author.name}>
      <div class="flex w-[300px] items-center justify-between gap-2">
        <Text>{author.name}</Text>
        <Text variant="secondary">{author.title}</Text>
      </div>
    </Select.Option>
  {/each}
</Select.Root>

Disabled Options

Options can be disabled with the disabled prop. Disabled options are greyed out and cannot be selected.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const regions = [
    { value: "us-east", label: "US East" },
    { value: "us-west", label: "US West" },
    { value: "eu-west", label: "EU West", disabled: true },
    { value: "ap-south", label: "AP South", disabled: true },
  ];

  let value = $state("");
</script>

<Select.Root
  label="Deployment Region"
  placeholder="Choose a region..."
  class="w-[250px]"
  {value}
  onValueChange={(next) => (value = next as string)}
>
  {#each regions as region (region.value)}
    <Select.Option value={region.value} disabled={region.disabled}>{region.label}</Select.Option>
  {/each}
</Select.Root>

Disabled Items (via items prop)

The items object-map prop accepts descriptor objects with disabled alongside plain string values.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  let value = $state("free");
</script>

<Select.Root
  label="Plan"
  class="w-[200px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  items={{
    free: "Free",
    pro: "Pro",
    business: { label: "Business", disabled: true },
    enterprise: { label: "Enterprise", disabled: true },
  }}
/>

Grouped Options

Use Select.Group, Select.GroupLabel, and Select.Separator to organize options under labeled headers with visual dividers.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const foods = {
    fruits: [
      { value: "apple", label: "Apple" },
      { value: "banana", label: "Banana" },
      { value: "cherry", label: "Cherry" },
    ],
    vegetables: [
      { value: "carrot", label: "Carrot" },
      { value: "broccoli", label: "Broccoli" },
      { value: "spinach", label: "Spinach" },
    ],
  };

  let value = $state("");
</script>

<Select.Root
  label="Food"
  placeholder="Pick a food..."
  class="w-[220px]"
  {value}
  onValueChange={(next) => (value = next as string)}
>
  <Select.Group>
    <Select.GroupLabel>Fruits</Select.GroupLabel>
    {#each foods.fruits as food (food.value)}
      <Select.Option value={food.value}>{food.label}</Select.Option>
    {/each}
  </Select.Group>
  <Select.Separator />
  <Select.Group>
    <Select.GroupLabel>Vegetables</Select.GroupLabel>
    {#each foods.vegetables as food (food.value)}
      <Select.Option value={food.value}>{food.label}</Select.Option>
    {/each}
  </Select.Group>
</Select.Root>

Groups with Disabled Options

Combine groups, separators, and disabled options to separate available and unavailable choices.

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const regions = {
    available: [
      { value: "us-east-1", label: "US East (N. Virginia)" },
      { value: "us-west-2", label: "US West (Oregon)" },
      { value: "eu-west-1", label: "EU West (Ireland)" },
    ],
    unavailable: [
      { value: "ap-south-1", label: "AP South (Mumbai)" },
      { value: "sa-east-1", label: "SA East (São Paulo)" },
    ],
  };

  let value = $state("");
</script>

<Select.Root
  label="Server Region"
  placeholder="Select a region..."
  class="w-[260px]"
  {value}
  onValueChange={(next) => (value = next as string)}
>
  <Select.Group>
    <Select.GroupLabel>Available</Select.GroupLabel>
    {#each regions.available as region (region.value)}
      <Select.Option value={region.value}>{region.label}</Select.Option>
    {/each}
  </Select.Group>
  <Select.Separator />
  <Select.Group>
    <Select.GroupLabel>Unavailable</Select.GroupLabel>
    {#each regions.unavailable as region (region.value)}
      <Select.Option value={region.value} disabled>{region.label}</Select.Option>
    {/each}
  </Select.Group>
</Select.Root>

Long List (Scrolling Test)

A select component with many options to test popup scrolling behavior.

Tests scrolling behavior with many options

<script lang="ts">
  import * as Select from "kumo-svelte/components/select";
  const items = Array.from({ length: 50 }, (_, index) => ({
    value: `item-${index + 1}`,
    label: `Option ${index + 1}`,
  }));

  let value = $state("");
</script>

<Select.Root
  label="Long List Select"
  description="Tests scrolling behavior with many options"
  placeholder="Choose an option..."
  class="w-[220px]"
  {value}
  onValueChange={(next) => (value = next as string)}
  {items}
/>

API Reference

Select

PropTypeDefault
aria-labelstring—
aria-labelledbystring—
allowDeselectbooleanfalse
childrenSnippet—
classstring—
containerPortalProps["to"]—
defaultValuestring | string[]—
descriptionSnippet | string—
disabledbooleanfalse
errorstring | { message: Snippet | string; match: FieldErrorMatch }—
hideLabelbooleanfalse
itemsSelectItems—
labelSnippet | string—
labelTooltipSnippet—
loadingbooleanfalse
multiplebooleanfalse
namestring—
onValueChange(value: string | string[]) => void—
placeholderstring—
requiredboolean—
sizeKumoSelectSizeKUMO_SELECT_DEFAULT_VARIANTS.size
valuestring | string[]—

Select.Content

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.Group

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.GroupHeading

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.Item

PropTypeDefault
childrenSnippet—
classstring—
disabledbooleanfalse
labelstring—
value*string—

Select.Portal

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.Separator

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.Trigger

PropTypeDefault
iconSnippet—
loadingbooleanfalse
sizeKumoSelectSizeKUMO_SELECT_DEFAULT_VARIANTS.size
showIconbooleantrue

Select.Value

No component-specific props. This component accepts child content or standard forwarded attributes.

Select.Viewport

No component-specific props. This component accepts child content or standard forwarded attributes.

Option Values

Select values are strings. For rich option rows, pass custom Select.Option children and a string label for the trigger display.

On this page

  • Import
  • Usage
  • Examples
    • Basic
    • Sizes
    • Without Visible Label
    • With Description
    • With Error
    • Placeholder
    • Label with Tooltip
    • Custom Rendering
    • Loading
    • Multiple Selection
    • More Example
    • Disabled Options
    • Disabled Items (via items prop)
    • Grouped Options
    • Groups with Disabled Options
    • Long List (Scrolling Test)
  • API Reference
    • Select
    • Select.Content
    • Select.Group
    • Select.GroupHeading
    • Select.Item
    • Select.Portal
    • Select.Separator
    • Select.Trigger
    • Select.Value
    • Select.Viewport
  • Option Values