Klarhimmel UI

Native select

Stable

The browser's own select in the system's field style. Best on phones and for short lists.

npx shadcn add @klarhimmel/native-select @klarhimmel/input

When to use

  • Use for

    One value from a short, fixed list, especially on phones.

  • Not for

    Long searchable lists, or options with icons.

  • Pairs with

    Field, forms and filters.

Preview

You can change this later.

native-select.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Field } from '@/components/klarhimmel/ui/input';
import { NativeSelect } from '@/components/klarhimmel/ui/native-select';

export default function Example() {
  return (
    <Theme theme="brand">
      <Field label="Room type">
        {(p) => (
          <NativeSelect {...p}>
            <option>Single</option>
            <option>Double</option>
          </NativeSelect>
        )}
      </Field>
    </Theme>
  );
}

Install

Import

import { NativeSelect } from '@/components/klarhimmel/ui/native-select';
Lands in
components/klarhimmel/ui/native-select.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.

Examples

  • Form

    Breakfast is included in every room.

    Chosen: double.

    import { useState } from 'react';
    import { Button } from '@/components/klarhimmel/ui/button';
    import { Field } from '@/components/klarhimmel/ui/input';
    import { NativeSelect } from '@/components/klarhimmel/ui/native-select';
    
    export default function NativeSelectForm() {
      const [type, setType] = useState('double');
      return (
        <div className="flex w-full max-w-xs flex-col gap-4">
          <Field label="Room type" hint="Breakfast is included in every room.">
            {(p) => (
              <NativeSelect value={type} onChange={(e) => setType(e.target.value)} {...p}>
                <option value="single">Single</option>
                <option value="double">Double</option>
                <option value="suite">Suite</option>
              </NativeSelect>
            )}
          </Field>
          <p aria-live="polite" className="text-kh-small text-kh-muted">Chosen: {type}.</p>
          <Button variant="secondary" onClick={() => setType('double')}>Reset</Button>
        </div>
      );
    }
  • States

    Pick a month to continue.

    import { Field } from '@/components/klarhimmel/ui/input';
    import { NativeSelect } from '@/components/klarhimmel/ui/native-select';
    
    export default function NativeSelectStates() {
      return (
        <div className="flex w-full max-w-xs flex-col gap-4">
          <Field label="Arrival month" error="Pick a month to continue.">
            {(p) => (
              <NativeSelect defaultValue="" {...p}>
                <option value="" disabled>Choose a month</option>
                <option>June</option>
                <option>July</option>
              </NativeSelect>
            )}
          </Field>
          <Field label="Country">
            {(p) => (
              <NativeSelect disabled defaultValue="se" {...p}>
                <option value="se">Sweden</option>
              </NativeSelect>
            )}
          </Field>
        </div>
      );
    }

API reference

PropTypeDefaultDescription
invalidbooleanfalseSets aria-invalid and the danger border.
...selectSelectHTMLAttributes-Every native select attribute is passed through; children are the options.
classNamestring-Applied to the wrapper, so you can set its width.

Accessibility

KeyDoes
Space / Enter / Up / DownOpens the browser's picker
Type a letterJumps to a matching option

The browser draws the open list, so it is always accessible and always native.

Accessibility