CompactSelect
A small Select with no floating label, for table rows, pagers and toolbars.
SourcePackage @weft-js/warpPlayground
Preview
tsx
import { CompactSelect } from '@weft-js/warp'export function Basic() { return ( <CompactSelect label="Region" options={REGION_OPTIONS} defaultValue="eu-west" /> )}const REGIONS = [ { region: 'eu-west', requests: 34.2, share: 40 }, { region: 'us-east', requests: 24.8, share: 29 }, { region: 'ap-south', requests: 12.1, share: 14 }, { region: 'us-west', requests: 9.4, share: 11 }, { region: 'eu-north', requests: 5.6, share: 6 },]const REGION_OPTIONS = REGIONS.map(r => ({ value: r.region, label: r.region }))Installation
npm install @weft-js/warpUsage
tsx
import { CompactSelect } from '@weft-js/warp'<CompactSelect label="Region" options={REGION_OPTIONS} defaultValue="eu-west" />States
A compact select can show a placeholder when empty, hold a chosen value, or be disabled.
tsx
import { CompactSelect } from '@weft-js/warp'export function States() { return ( <> <CompactSelect label="Region" placeholder="Region" options={REGION_OPTIONS} /> <CompactSelect label="Region" options={REGION_OPTIONS} defaultValue="us-east" /> <CompactSelect label="Region" options={REGION_OPTIONS} defaultValue="us-east" disabled /> </> )}const REGIONS = [ { region: 'eu-west', requests: 34.2, share: 40 }, { region: 'us-east', requests: 24.8, share: 29 }, { region: 'ap-south', requests: 12.1, share: 14 }, { region: 'us-west', requests: 9.4, share: 11 }, { region: 'eu-north', requests: 5.6, share: 6 },]const REGION_OPTIONS = REGIONS.map(r => ({ value: r.region, label: r.region }))Rows per page
A compact select suits a small choice beside other content, here the number of rows per page.
tsx
import { derived, state } from '@weft-js/core'import { CompactSelect, Row, Stack, Text } from '@weft-js/warp'export function Rows() { const size = state<string | null>('5') const rows = derived(() => PROJECTS.slice(0, Number(size.value))) return ( <Stack gap="group"> <Stack gap="tight"> {rows.value.map(p => <Row key={p.id} justify="between"><Text>{p.name}</Text><Text subtle>{p.owner}</Text></Row>)} </Stack> <Row justify="between"> <Text subtle>{rows.value.length} of {PROJECTS.length}</Text> <CompactSelect label="Rows per page" options={PAGE_SIZES} value={size} /> </Row> </Stack> )}type Status = 'Live' | 'Building' | 'Failed' | 'Paused'const PROJECTS: { id: string; name: string; owner: string; region: string; status: Status; deploys: number; uptime: number; latency: number; cost: number; updated: string }[] = [ { id: 'storefront', name: 'storefront', owner: 'Priya Raman', region: 'eu-west', status: 'Live', deploys: 412, uptime: 99.98, latency: 84, cost: 1240, updated: '2026-10-31' }, { id: 'checkout', name: 'checkout', owner: 'Tomás Oliveira', region: 'eu-west', status: 'Live', deploys: 288, uptime: 99.95, latency: 112, cost: 980, updated: '2026-10-31' }, { id: 'search-api', name: 'search-api', owner: 'Lena Fischer', region: 'us-east', status: 'Building', deploys: 196, uptime: 99.91, latency: 146, cost: 1610, updated: '2026-10-30' }, { id: 'billing', name: 'billing', owner: 'Mara Lindqvist', region: 'us-east', status: 'Live', deploys: 154, uptime: 99.99, latency: 73, cost: 720, updated: '2026-10-29' }, { id: 'mobile-bff', name: 'mobile-bff', owner: 'Sam Okafor', region: 'ap-south', status: 'Failed', deploys: 233, uptime: 99.42, latency: 208, cost: 860, updated: '2026-10-29' }, { id: 'notifier', name: 'notifier', owner: 'Jun Tanaka', region: 'eu-north', status: 'Live', deploys: 97, uptime: 99.97, latency: 58, cost: 310, updated: '2026-10-27' }, { id: 'reports', name: 'reports', owner: 'Noor Haddad', region: 'us-west', status: 'Paused', deploys: 41, uptime: 99.8, latency: 321, cost: 190, updated: '2026-10-22' }, { id: 'edge-cache', name: 'edge-cache', owner: 'Jun Tanaka', region: 'global', status: 'Live', deploys: 318, uptime: 100, latency: 21, cost: 1430, updated: '2026-10-31' }, { id: 'auth', name: 'auth', owner: 'Mara Lindqvist', region: 'eu-west', status: 'Live', deploys: 176, uptime: 99.99, latency: 66, cost: 540, updated: '2026-10-28' }, { id: 'ingest', name: 'ingest', owner: 'Lena Fischer', region: 'us-east', status: 'Live', deploys: 264, uptime: 99.93, latency: 131, cost: 2120, updated: '2026-10-30' },]const PAGE_SIZES = [{ value: '3', label: '3 rows' }, { value: '5', label: '5 rows' }, { value: '10', label: '10 rows' }]A role in each row
Give each row its own compact select, with defaultValue set to the person's current role.
tsx
import { CompactSelect, Row, Stack, Text } from '@weft-js/warp'import type { Role } from '@weft-js/warp'export function Roles() { return ( <Stack gap="related" divided> {TEAM.map(p => ( <Row key={p.id} justify="between" gap="group"> <Stack gap="hair"> <Text>{p.name}</Text> <Text role="caption" subtle>{p.email}</Text> </Stack> <CompactSelect label={`Role for ${p.name}`} options={TEAM_ROLES} defaultValue={p.role} /> </Row> ))} </Stack> )}const PEOPLE = [ { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' }, { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' }, { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' }, { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' }, { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' }, { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' }, { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' }, { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const ROLE_OPTIONS = [ { value: 'Admin', label: 'Admin', hint: 'Manages people and billing' }, { value: 'Member', label: 'Member', hint: 'Deploys and edits projects' }, { value: 'Viewer', label: 'Viewer', hint: 'Reads logs and metrics' },]const TEAM_ROLES = ROLE_OPTIONS.map(r => ({ value: r.value, label: r.value }))const TEAM = PEOPLE.slice(1, 5)When to use
Playground
import { CompactSelect } from '@weft-js/warp'<CompactSelect options={REGIONS} label="Region" />Accessibility
- EnterSpace
- Opens, or picks the highlighted option
- ↑↓
- Moves through the options
- Esc
- Closes and returns focus
- With nothing drawn for
label, make it specific: "Role for Jun Tanaka", not "Role".
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | The accessible name. Not drawn. |
options | (Option | OptionGroup)[] | — | The list, the same as Select. |
placeholder | string | — | Sample text shown while empty. Never a substitute for a label. |
value | string | State<string | null> | null | — | A string, or a State to bind. |
defaultValue | string | null | — | The starting value, when the component owns the state. |
open | boolean | State<boolean> | — | A state box that holds whether it is open. |
defaultOpen | boolean | false | Starts open, when the component keeps its own open state. |
presentation | Presentation | — | auto opens a popover, or a sheet when the viewport is under 600px wide; popover and sheet force one. |
searchable | boolean | — | Puts a search field at the top of the list. On by itself past 15 options. |
name | string | — | Puts it in a form with a hidden input. |
id | string | — | The element id. One is made for you if you leave it out. |
disabled | boolean | false | Turns it off, and takes it out of the tab order. |
describedby | string | — | The id of text that explains it. |
| Event | Type | Default | Description |
|---|---|---|---|
onValueChange | ((value: string, reason: ChangeReason) => void) | — | Called with the picked option's value, and whether a pointer, a key or code picked it. |
onOpenChange | ((open: boolean) => void) | — | Called with true when the list opens and false when it closes. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="field"] [data-part="control"]
controlsweep-basesweepinputvalue