Accordion

Bits UI components

Accordion

Disclosure & Layout
Local guide ↗ import { Accordion } from "bits-ui"

Single

type="single" keeps at most one section open, with animated height via --bits-accordion-content-height

Multiple, controlled

type="multiple" with bind:value — external buttons drive the same state

1 open

Two-way bound state for shipping. The buttons above write straight into the same $state array the accordion reads.

type is required and switches the shape of value: a string for "single", a string array for "multiple". Because a bindable prop has to be destructured, wrapper components typically spread {...restProps as any} to escape the discriminated union.

Collapsible

Disclosure & Layout
Local guide ↗ import { Collapsible } from "bits-ui"

Animated content

Height and opacity animate without measuring in application code

Controlled and searchable

Bound open state plus hidden-until-found browser search integration

State: closed

Content exposes --bits-collapsible-content-height and --bits-collapsible-content-width, so CSS can animate to the measured size. For Svelte transitions instead, set forceMount and render conditionally from the child snippet's open value.

hiddenUntilFound maps collapsed content to hidden="until-found", allowing supported browsers to reveal it for find-in-page matches.

Tabs

Disclosure & Layout
Local guide ↗ import { Tabs } from "bits-ui"

Release channels

Automatic horizontal tabs with matched trigger and panel values

2.19.0

Recommended

Production-ready APIs with documented migration paths.

Vertical, manual activation

Arrow keys move focus; Enter or Space commits the focused tab

Appearance

Use the system theme and compact density on this device.

manual · use ↑/↓, then Enter or Space

Every Trigger and Content requires a matching string value. Orientation also changes keyboard navigation: horizontal lists use Left/Right, while vertical lists use Up/Down.

Activation defaults to automatic, which selects a tab as focus moves. Set activationMode="manual" when focus movement should preview nothing until the user presses the trigger; keyboard navigation loops by default.

Aspect Ratio

Disclosure & Layout
Local guide ↗ import { AspectRatio } from "bits-ui"

Widescreen artwork

A dependency-free 16:9 visual built from layered CSS gradients

16 : 9

Signal study 04

Depth without an image request

Ratio comparison

The same width constraint produces 4:3 and square media frames

4 : 3 editorial

Landscape card

1 : 1 cover

Square artwork

ratio is a number expressed as width divided by height, such as 16 / 9 or 4 / 3; it defaults to 1. The primitive owns only the sizing logic, so media still needs its own full-size and object-fit styling.

Scroll Area

Disclosure & Layout
Local guide ↗ import { ScrollArea } from "bits-ui"

Visibility types

Hover, scroll, auto and always use the same viewport with different scrollbar lifecycles

hover
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
scroll
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
auto
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04
always
Build queued1:00
Dependencies restored2:01
Routes compiled3:02
Assets optimized4:03
Health check passed5:04

Horizontal collection

A persistent horizontal scrollbar for a wide row of fixed-size cards

01

Research

Owner · Nora

02

Prototype

Owner · Ishan

03

Review

Owner · Mara

04

Release

Owner · Theo

type defaults to hover. In scroll mode bars appear during scrolling; auto keeps them visible only when content overflows, while always shows them even without overflow.

Each Scrollbar requires an orientation and its own Thumb. Add Corner where horizontal and vertical bars meet. The scrollHideDelay default is 600 ms and applies to hover and scroll modes.

Separator

Disclosure & Layout
Local guide ↗ import { Separator } from "bits-ui"

Semantic horizontal divider

A content boundary that remains available to assistive technology

Environment

Production cluster

eu-west · 12 healthy services

Uptime99.99%
Latency42 ms
Regions4

Decorative vertical dividers

Visual rhythm between related links without extra screen-reader output

Orientation defaults to horizontal and is reflected by data-orientation for styling. Set orientation="vertical" explicitly and give the element a usable height for vertical layouts.

decorative defaults to false, so the separator is announced by screen readers. Use decorative only when the line is purely visual and the surrounding structure already conveys the grouping.

Toolbar

Disclosure & Layout
Local guide ↗ import { Toolbar } from "bits-ui"

Editing toolbar

Independent multiple- and single-value groups alongside a button and link

marks=[bold] · align=left · Ready

Vertical controls

A non-looping vertical toolbar with controlled view selection

preview view

No history action

vertical · loop=false

Toolbar.Group requires type: single groups bind a string, while multiple groups bind a string array. Every GroupItem also requires the string value contributed to that state.

The root defaults to horizontal orientation and looping keyboard navigation. Set orientation="vertical" for a column and loop={false} when focus should stop at the first and last controls. Use Button for actions and Link for navigation so each retains its native element semantics.

Dialog

Overlays
Local guide ↗ import { Dialog } from "bits-ui"

Form dialog

A labelled modal form with controlled open state and predictable cancellation

Scrollable content

A bounded modal keeps its title and actions visible while the document body scrolls

Dialog.Portal moves the overlay and content to document.body by default, escaping local stacking contexts. Modal content traps focus, locks body scrolling, focuses the first focusable control on open, and restores focus to the trigger on close. Keep Dialog.Title and Dialog.Description inside the content so their generated IDs can label the dialog for assistive technology.

Escape and outside interaction close a dialog by default. Configure escapeKeydownBehavior or interactOutsideBehavior on Dialog.Content only when the workflow truly requires it; the corresponding event callbacks can prevent a single dismissal. Set trapFocus={false} or preventScroll={false} only with an alternative accessibility strategy.

Alert Dialog

Overlays
Local guide ↗ import { AlertDialog } from "bits-ui"

Destructive confirmation

Cancel exits without committing; Action confirms the irreversible operation

No action taken

Controlled state

An external control opens the alert while Cancel and Action retain distinct semantics

Analytics workspace

Current access: Editor

AlertDialog.Portal renders into document.body by default, so a high z-index on the portalled overlay/content clears page-level stacking contexts. Alert dialogs trap focus and lock body scrolling; unlike a regular dialog, initial focus lands on the content so a screen reader announces the warning. Focus returns to the trigger when the alert closes.

Escape closes by default, but outside interaction is ignored by default to prevent accidental confirmation dismissal. Those policies are configurable through escapeKeydownBehavior and interactOutsideBehavior on AlertDialog.Content. Use Cancel for the no-op path and Action only for the committing path; both close the alert.

Popover

Overlays
Local guide ↗ import { Popover } from "bits-ui"

Anchored form

Interactive content anchored below its trigger with a visible positioning arrow

Side and alignment

The same floating panel uses explicit side, align, and sideOffset combinations

Popover.Portal moves floating content to document.body by default; Content still measures and follows its trigger through Floating UI. The resolved placement appears in data-side and data-align, while --bits-popover-content-transform-origin supports placement-aware animation.

Popover content traps focus by default, focuses its first focusable control on open, and restores focus to the trigger on close. Escape and outside pointer-down both close it by default; configure escapeKeydownBehavior, interactOutsideBehavior, or their cancellable event callbacks on Popover.Content to change that. Unlike dialogs, popovers do not lock body scrolling or block outside interaction unless preventScroll is enabled.

Tooltip

Overlays
Local guide ↗ import { Tooltip } from "bits-ui"

Shared provider

One Provider supplies a 250ms delay and non-hoverable content policy to a toolbar

Keyboard focus

Focusable triggers reveal the same descriptions without requiring hover

Press Tab until either control is focused. No pointer is required.

Tooltip.Provider is required above every root. Its delayDuration and disableHoverableContent defaults flow to all nested tooltips, and it ensures only one tooltip in that provider is open. A Tooltip.Portal renders content into document.body by default, avoiding clipped toolbars and local stacking contexts.

A tooltip does not move or trap keyboard focus; focus stays on its trigger, so its content must be descriptive rather than an interactive mini-dialog. Escape or an outside pointer-down closes the content by default, and pressing the trigger with any pointer button also closes it. Use ignoreNonKeyboardFocus when focus caused by a pointer should not open the tooltip; data-state distinguishes delayed, instant, and closed states for animation.

Context Menu

Menus & Navigation
Local guide ↗ import { ContextMenu } from "bits-ui"

Context actions

Right-click the target surface to open actions at the pointer

Deployment card

Right-click anywhere on this surface

No action yet

View preferences

Checkbox state and a nested preview-size submenu stay inside the contextual workflow

Right-click the canvas for view options

The Trigger owns the right-click surface rather than a separate button. Once open, arrow keys move the highlighted item, Enter or Space selects, and Escape dismisses; disabled items are skipped. Item, CheckboxItem, RadioItem, and SubTrigger all accept textValue for typeahead when their rendered label is not plain text.

Content is portalled to document.body by default and uses the pointer location as its anchor. CheckboxItem exposes bindable checked and indeterminate states; its children snippet receives the resolved checked state for rendering an indicator.

Command

Menus & Navigation
Local guide ↗ import { Command } from "bits-ui"

Inline palette

Built-in ranked filtering includes each item’s value, text and keywords

No command run yet

Dialog palette

A modal command surface toggled by its trigger or Cmd/Ctrl+K

Press Cmd/Ctrl+K while this demo is mounted.

Each Command.Item requires a unique value; optional keywords add aliases to the default scoring filter. A custom filter must return a score from 0 to 1, where 0 hides the item entirely.

The list height computed by Command.Viewport is exposed as --bits-command-list-height. Cmd/Ctrl+K is an application convention rather than a Command prop; register it only for the owning surface and remove the listener when that surface is destroyed.

Pagination

Menus & Navigation
Local guide ↗ import { Pagination } from "bits-ui"

Controlled results

bind:page stays synchronized with page triggers and external controls

Audit log

Current page: 6

Showing 101–120 of 247 events

Compact

siblingCount=0 keeps the control useful in a narrow toolbar

……

Page 3 of 12

count is the required total item count; pagination derives its page count from perPage, which defaults to 1. The children snippet receives pages, range, and currentPage, so summary text and controls share the same calculation.

Entries in pages are a discriminated union: render type="page" with Pagination.Page, but render type="ellipsis" yourself. Horizontal and vertical orientation also determine the component’s arrow-key behavior.

Button

Form Controls
Local guide ↗ import { Button } from "bits-ui"

Action variants

One behavioral primitive, styled for primary, secondary, destructive and disabled states

Choose an action

Links as buttons

Adding href changes the underlying element from button to anchor

Navigation action

Passing href makes the root render an anchor while retaining the button skin.

Explore Command

External destination

Standard anchor attributes pass through to the rendered element.

Local Button guide

Button.Root chooses its element from its props: without href it is a button, while supplying href renders an anchor for real navigation semantics. Use the link form for destinations, not merely to make an action look like a link.

The disabled prop prevents interaction and defaults to false. Every root exposes data-button-root, giving every visual variant a stable styling hook without duplicating behavior.

Checkbox

Form Controls
Local guide ↗ import { Checkbox } from "bits-ui"

Three states

Bind checked and indeterminate independently; data-state exposes every visual state

indeterminate

Parent and children

A derived parent state controls a Checkbox.Group without duplicating child state

2/3

Required form value

name creates the hidden input and value controls the submitted payload

Required before the release can be submitted.

Submit to inspect the form value

checked and indeterminate are separate bindable booleans. The root reports all three visual states through data-state, while its children snippet receives both values for icon rendering.

Passing name renders a hidden checkbox input; value replaces its default "on" payload, and required is forwarded to that hidden input. A Checkbox.Group propagates its name, required, disabled, and readonly settings to descendant roots and submits one same-named hidden input per selected value.

Switch

Form Controls
Local guide ↗ import { Switch } from "bits-ui"

Settings row

A labelled binary setting with a moving thumb and explicit status

A concise activity summary every Monday morning.

On

Controlled and disabled

External controls update the same bound state; managed settings remain visibly unavailable

Controlled state: off

Managed by your organization

Both Switch.Root and Switch.Thumb expose data-state="checked|unchecked". The root also exposes data-disabled; bind checked when an external control must drive the switch.

A name on the root creates a hidden input for form submission. Its checked payload defaults to "on", value supplies a custom payload, and required is applied to that hidden input so native form validation can require the switch to be on.

Radio Group

Form Controls
Local guide ↗ import { RadioGroup } from "bits-ui"

Plan cards

The item itself is the card, so data-state can style the complete checked surface

Selected plan: team

Keyboard orientation

Orientation changes both layout and the arrow keys used by roving focus

Use Left / Right arrows

Priority: Normal

Every RadioGroup.Item requires a unique string value. The root defaults to vertical orientation: vertical groups use Up/Down, horizontal groups use Left/Right, and loop controls whether arrow navigation wraps.

Setting name on the root renders the hidden input that submits the selected string. required is applied to that hidden input for native form validation. A readonly group remains focusable and navigable but cannot change value; a disabled group is non-focusable and non-interactive.

Label

Form Controls
Local guide ↗ import { Label } from "bits-ui"

Native input

for points to the native input id, so the text names and activates the field

Notifications will be sent to alex@northstar.dev.

Bits UI control

for/id activation and aria-labelledby give the custom checkbox one shared visible name

A concise summary of deployments, incidents and open reviews.

Digest is disabled.

Label.Root is an enhanced label intended for any input. Association is still explicit: pair for with the control’s id; for a non-native Bits UI control, also use the label id with aria-labelledby when the control’s API calls for an accessible name.

Unlike a bare label used ad hoc around a custom control, the primitive supports Bits UI child delegation and a bindable element ref, and exposes data-label-root as a stable styling hook. Those integrations do not replace the accessible association—the matching ids are what make clicking the visible label activate its control.

Toggle

Form Controls
Local guide ↗ import { Toggle } from "bits-ui"

Icon toggle

bind:pressed synchronizes the icon, revealed value, status and pressed surface

•••••••••••••• Hidden

Controlled and locked

External controls share the pressed state; a disabled toggle communicates a fixed policy

Alerts: muted
Security alerts are locked on

pressed is the bindable boolean state. The root emits data-state="on" or data-state="off", and adds data-disabled when interaction is disabled. Icon-only toggles still need an accessible name such as aria-label.

Unlike Checkbox, Switch, and Radio Group, Toggle has no name, value, or required form props and does not render a hidden input. Mirror its pressed state into your own form field when a toggle must be submitted.

Toggle Group

Form Controls
Local guide ↗ import { ToggleGroup } from "bits-ui"

Single selection

type="single" models mutually exclusive alignment with a string value

Alignment: left

Multiple selection

type="multiple" keeps independent text formats in a string array

Ship precise interfaces, not piles of configuration.

Active: bold, underline

type is required and forms a discriminated union: "single" binds a string while "multiple" binds a string array. Each item exposes data-state="on|off" for pressed styling.

The root defaults to horizontal orientation, looping arrow navigation, and roving focus. Set orientation, loop, or rovingFocus to change those keyboard semantics. Toggle Group has no name or required props and does not create hidden form inputs; serialize its bound value separately when submitting a form.

Select

Selection & Input
Local guide ↗ import { Select } from "bits-ui"

Grouped, single value

A bound string drives the trigger label while Group and GroupHeading give related options accessible structure

Submitted value: guided

Multiple values

type="multiple" changes value to string[] and keeps the list open for successive choices

Alert channels 2 selected
Email digestSlack alerts

type is required and discriminates the value shape: "single" binds a string, while "multiple" binds a string array. Supplying items gives Bits UI the value/label pairs it needs for closed-trigger typeahead and single-select form autofill.

Set name to render the hidden form input. A required select must provide both required and name. Content is portalled to document.body by default, and exposes anchor width and available-height CSS variables for collision-safe sizing.

Combobox

Selection & Input
Local guide ↗ import { Combobox } from "bits-ui"

Filtered list

The input owns the query, an each/else block supplies the empty state, and the selected value remains separately bound

No deployment region selected

Multiple with chips

A string-array selection is projected into removable chips without conflating selected values with the search text

Reviewer expertise 2 selected
Accessibility Svelte

type is required: single mode binds a string and multiple mode binds a string array. Filtering is intentionally application-owned; update a query from Combobox.Input and render only matching items. The input retains DOM focus while keyboard navigation marks items with data-highlighted.

Providing name creates the hidden form input. For single mode, an items value/label array also enables browser autofill; use inputValue when an externally changed selection must force new display text. Portalled content exposes anchor-size and available-space CSS variables for sizing.

Slider

Selection & Input
Local guide ↗ import { Slider } from "bits-ui"

Single value

A numeric value updates continuously as the thumb moves

Output volume 62%
QuietFull

Range

type="multiple" binds number[]; the thumbs snippet supplies the indexes to render

Monthly infrastructure budget $25k–$70k
$25k $70k

Drag either thumb; values stay sorted as the handles cross.

Discrete steps and ticks

An array-valued step prop creates a non-linear scale, with thumbs, ticks and tickItems supplied to the children snippet

Editor font size

Discrete scale, not a continuous interval

16px
8 12 16 24 32 48

type is required: single mode binds a number; multiple mode binds a number array and automatically sorts crossed thumbs unless autoSort={false}. The root children snippet exposes thumb indexes and tick indexes; tickItems adds each tick’s numeric value. An array passed to step defines discrete allowed values rather than a uniform increment.

Slider deliberately renders no hidden form input because its value space is effectively unbounded. Add your own hidden input for a single value, or one input per range endpoint. Use onValueCommit for expensive work after a drag; onValueChange runs throughout it.

PIN Input

Selection & Input
Local guide ↗ import { PinInput } from "bits-ui"

Six-digit OTP

Numeric input, sanitizing paste and an observable completion callback share one invisible native input

Paste “482-901” or type six digits.

0 of 6 digits entered

Form submission

The visual cells submit through the named native input rather than concatenating six separate fields

Enter the four-digit code from your authenticator.

The root owns one string value and its children snippet supplies a cells array. Each cell reports char, isActive and hasFakeCaret; pass that object unchanged to PinInput.Cell. A pattern rejects invalid input, while pasteTransformer must return the sanitized text Bits UI should actually store.

The cells are presentation around an invisible native input. Set name to include its string value in form data, inputId to connect a label to that real input, and bind inputRef when imperative focus or selection access is needed. onComplete fires when maxlength cells are filled.

Rating Group

Selection & Input
Local guide ↗ import { RatingGroup } from "bits-ui"

Interactive half ratings

The items snippet exposes active, partial and inactive visual states while the bound value remains numeric

Release confidence

Half-step precision is enabled.

3.5 / 5

Read-only aggregate

Readonly preserves the same accessible value model without presenting an editable control

SvelteKit adapter

Aggregate from 1,284 verified projects

4.5 average

The bound value is always a number. allowHalf changes pointer and arrow-key precision to 0.5, and each item in the root snippet reports "inactive", "active" or "partial". Use those states for the artwork rather than recomputing fill from the value. max also controls how many item records are generated.

This component follows the ARIA slider pattern: the root is the single focus stop and items are presentational. Set name to render a hidden form input containing the numeric value; required marks that input required. aria-valuetext accepts either a string or a function of the current value and maximum.

Calendar

Dates & Time
Local guide ↗ import { Calendar } from "bits-ui"

Single date

type="single" binds one DateValue while the grid exposes selection and today states

Selected date Friday, October 30, 2026
SunMonTueWedThuFriSat
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Two-month planning

numberOfMonths renders adjacent grids; pagedNavigation advances them as one view

Roadmap planning

Navigation advances by both visible months.

Nov 9, 2026
SMTWTFS
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
SMTWTFS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12

Bounded availability

minValue and maxValue bound the window while isDateUnavailable marks individual dates

Installation window

Nov 6

Book from Oct 30 through Nov 20.

Selected Unavailable Out of window
SMTWTFS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5

Calendar state uses immutable DateValue objects from @internationalized/date, not native Date instances. The required type prop also changes the value shape: "single" uses one value, while "multiple" uses an array.

placeholder is navigation state: it chooses the visible month when no selection exists and updates as the user pages. It is independent from value, which is the actual selection. Convert to a native date with an explicit time zone only at the formatting or integration boundary.

Range Calendar

Dates & Time
Local guide ↗ import { RangeCalendar } from "bits-ui"

Booking range

A two-month availability view with formatted start and end values

Check in

Nov 1

Check out

Nov 5
SMTWTFS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
SMTWTFS
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
Range boundary Stay Sold out

Length and exclusion rules

minDays and maxDays bound the stay; excludeDisabled rejects ranges that cross maintenance

Workshop residency

Nov 1 – Nov 5

Select between 3 and 7 days. The maintenance date cannot be crossed.

3-day minimum 7-day maximum Maintenance: Nov 8
SMTWTFS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5

The bound value is a pair of immutable DateValue objects from @internationalized/date: { start, end }. Either endpoint can be undefined while a range is being composed. The placeholder is separate navigation state that controls the visible month; it is not either selected endpoint in value.

excludeDisabled resets a candidate range that contains a date returned by isDateDisabled. It does not apply to isDateUnavailable dates; use the disabled predicate when a range must not cross the blocked date. Range boundaries and middle days expose separate data attributes for styling.

Date Field

Dates & Time
Local guide ↗ import { DateField } from "bits-ui"

Segmented date

Locale-aware month, day, and year segments with native form participation

Arrival date
11062026

Use the arrow keys to adjust the focused segment.

November 6, 2026

Custom validation

validate returns an error message while errorMessageId connects it to the field

Dispatch date
10312026

Current value

Saturday, October 31, 2026

Date and time

granularity="minute" adds hour and minute segments; readonlySegments locks the year

Design review

The year segment is visible but read-only.

Session date and time
103020260930
Oct 30, 2026, 9:30 AM

Date Field stores immutable DateValue objects from @internationalized/date. A CalendarDate defaults to day granularity; a date-time value defaults to minute granularity. The placeholder is not empty-field text: it seeds segment cycling and can determine the resulting value type, while value is the user's completed selection.

Every rendered segment must receive its part, including locale-specific literals. Setting name on DateField.Input adds the hidden input used by native form submission; errorMessageId associates custom validation text with the segmented field.

Date Picker

Dates & Time
Local guide ↗ import { DatePicker } from "bits-ui"

Field and calendar

A segmented field and popover calendar share one DateValue

Release date
11132026
Selected: Friday, November 13, 2026

Controlled review

bind:open exposes popover state; closeOnDateSelect=false keeps the calendar open for review

Security review

Choose a date, inspect it, then close explicitly.

popover: closed
Review date
11042026
November 4, 2026

Date Picker shares one immutable DateValue from @internationalized/date across its field and calendar. placeholder is not the selected value or empty-field text: it seeds segment editing and controls which month is visible when value is empty.

DatePicker.Portal renders content into document.body by default; pass disabled to keep it in place. Content exposes opening and closing data attributes plus positioning variables such as --bits-popover-content-available-height and --bits-popover-content-transform-origin.

Date Range Field

Dates & Time
Local guide ↗ import { DateRangeField } from "bits-ui"

Bound stay duration

The start and end segments share one bound range; external controls preserve its duration

Stay dates
4 nights

Date and time range

A CalendarDateTime range rendered to minute granularity with an explicit 24-hour cycle

Studio booking window
2026-09-18T16:30:00 → 2026-09-18T18:00:00

Each DateRangeField.Input requires a type of start or end; its optional name creates a hidden form input. The bound value is one object with start and end date values, and either side may be temporarily undefined while the user is entering it.

Granularity defaults to day for a CalendarDate and minute for CalendarDateTime. It controls which segments render, while locale controls their order and separators; hourCycle overrides the locale's 12- or 24-hour preference when time segments are present.

Date Range Picker

Dates & Time
Local guide ↗ import { DateRangePicker } from "bits-ui"

Presets and range calendar

Last 7 days and this month replace the same bound range edited by the field and calendar

Travel dates

Bound range: 2026-10-24 — 2026-10-30

Constrained stay

Minimum and maximum range lengths with the popover held open while dates are refined

Production reservation

Select 3–10 consecutive calendar days

5 days

Date Range Picker composes Date Range Field, Range Calendar, and Popover state. Its value is still one { start, end } object; presets must replace those immutable date values rather than mutate them. Selection closes the popover by default, while closeOnRangeSelect can keep it open.

The top-level Portal utility renders the picker content into document.body by default and accepts a custom to target. Locale controls field formatting and the calendar's first weekday unless weekStartsOn is set; granularity and hourCycle follow the same rules as Date Range Field when the bound values include time.

Time Field

Dates & Time
Local guide ↗ import { TimeField } from "bits-ui"

Bound time

A Time value with minute steps, a 12-hour cycle, and external 15-minute controls

Daily stand-up
0930AM
09:30:00

Value type and precision

Time and CalendarDateTime values rendered with different granularities and hour cycles

Render deadline
140735

Time · second precision · 24-hour

14:07:35
Release call
0645PM

CalendarDateTime · minute precision · 12-hour

2026-10-21T18:45:00

value may be a Time, CalendarDateTime, or ZonedDateTime, and the bound type is inferred from the supplied value. A CalendarDateTime keeps its date even though Time Field renders only its time segments. Supplying a name to TimeField.Input adds the hidden input used for form submission.

granularity is visual: hour, minute, or second determines the last rendered segment and defaults to minute. Locale changes segment ordering, separators, and the preferred clock; hourCycle explicitly overrides that preference. The placeholder is the starting time used when cycling an empty segment, not display-only hint text.

Time Range Field

Dates & Time
Local guide ↗ import { TimeRangeField } from "bits-ui"

Bound working hours

Start and end Time values move together while a derived duration stays in sync

Support coverage
8h 30m coverage

Cross-field validation

The complete range is rejected when its end is earlier than its start

Review session

End time must not be earlier than start time.

The bound value is { start, end }, where either side can be undefined during partial entry. Each TimeRangeField.Input therefore needs a required type so Bits UI can route its segments to the correct side; an Input name also produces that side's hidden form input.

Custom validation receives the completed range and may return a string or string array; errorMessageId associates explanatory text with the invalid field. Granularity defaults to minute, locale supplies segment order and clock preference, and hourCycle can force 12- or 24-hour output for both sides.

Avatar

Feedback
Local guide ↗ import { Avatar } from "bits-ui"

Loaded image

A self-contained SVG portrait with the root's live loading status

AM

Avery Morgan

Design systems

loading

Broken source fallback

An intentionally invalid local URL exposes loading, error and fallback behaviour

···

Deliberately unavailable source

loadingStatus: loading

Avatar group

Loaded data-URL portraits overlap without relying on a remote image host

Mina Lee
Jon Bell
Sam Ortiz
Ivy Chen

4 collaborators

Hover a portrait for the name

loadingStatus is bindable and moves through loading, loaded and error; the same value is exposed as data-status on all three parts. The fallback is rendered while loading and after an error. For a source known to be available, setting loadingStatus="loaded" skips the image load check; delayMs can otherwise delay revealing a loaded image.

Progress

Feedback
Local guide ↗ import { Progress } from "bits-ui"

Determinate

Controls advance a task toward a known end point

Uploading research-notes.pdf

Encrypted transfer

45%

Indeterminate

value={null} represents work whose completion cannot yet be calculated

Indexing workspace

The remaining work cannot be estimated yet

Working

value = null · data-state = indeterminate

value accepts a number or null. A null value changes data-state to indeterminate and adds data-indeterminate; numeric values are interpreted between min and max, which default to 0 and 100. A visible label should be connected with aria-labelledby, or replaced by an aria-label when no label is shown.

Meter

Feedback
Local guide ↗ import { Meter } from "bits-ui"

Disk usage thresholds

A capacity reading changes colour as healthy, high and critical thresholds are crossed

Workspace disk

Healthy usage

312 GB / 500 GB

Battery reserve

A fluctuating measurement with a domain-specific accessible value

Battery reserve

Estimated runtime at the current load

7.5 h
Known range: 0–24 hours

A meter reports a current measurement within a known range, so its value may move in either direction. Progress instead communicates advancement toward task completion and ordinarily moves forward. For units that are clearer than a percentage, provide aria-valuetext (for example, gigabytes used or hours remaining) and connect the visible label with aria-labelledby.

Portal

Utilities
Local guide ↗ import { Portal } from "bits-ui"

Named target

Move the same child between its declaration site and a specific DOM target

Invocation site

source tree

No child here in the DOM

Named target

#portal-demo-target

The child moves without changing where the Portal is declared.

Without a to prop, Portal appends its children to document.body. The target may instead be an Element or selector string; a named target is useful when an overlay must stay inside a particular stacking or theme boundary.

Set disabled to render at the original DOM location. The same Portal implementation backs the Portal sub-component exposed by Bits UI overlays, and BitsConfig can replace its default target for an entire subtree.

mergeProps

Utilities
Local guide ↗

Composable props

Chain handlers in order while merging class and style values into one spreadable object

Handler trace

Click the merged button to record call order.

Merged output

class: btn w-full justify-center border-brand-500 bg-ink-800 text-brand-400

style: border-width: 2px; padding-block: 0.8rem; letter-spacing: 0.08em; border-radius: 9999px;

Both handlers run in merge order.

mergeProps is the safe boundary between a headless component's behavior props and consumer props. Event handlers run in argument order, but calling event.preventDefault() stops every later event handler in the merged chain. Non-event functions are also chained, without cancellation semantics.

Classes are combined with clsx. Styles may be strings or objects and are normalized into one style string; when the same CSS property appears more than once, the later props object wins. This is also the merge behavior Bits UI uses internally for rest props and child-snippet props.

useId

Utilities
Local guide ↗

Accessible field IDs

Generate unique, hydration-stable IDs once and use them to connect labels to controls

bits-29645
bits-29646

Click either label to focus its control

unique IDs

Call useId() during component initialization, not in an event handler or a changing loop. The returned value can connect label[for] to a control's id, or wire ARIA relationships without requiring a caller-supplied identifier.

Bits UI uses the same generator internally for component relationships. Keeping the call order stable between server rendering and hydration keeps those IDs aligned; each call in one component instance returns a distinct value.

IsUsingKeyboard

Utilities
Local guide ↗

Live input modality

Observe the shared document-level state switch between keyboard and pointer input

Pointer modality

A pointer move or pointerdown was detected.

Try Tab or an arrow key, then move the pointer across the demo. The indicator follows the last input modality anywhere in the document.

IsUsingKeyboard.current becomes true after a document keydown, then false after pointerdown or pointermove. It describes the latest input modality, not whether an element merely has focus.

Instances share one global reactive state and reference-counted document listeners, so components can observe modality without each registering duplicate handlers. Bits UI uses this signal for keyboard accessibility behavior; applications can also use it to avoid pointer-only focus decoration.

BitsConfig

Utilities
Local guide ↗ import { BitsConfig } from "bits-ui"

Inherited defaults

A nested provider inherits the parent locale while overriding only its portal target

Parent target

#config-parent-target

Child target

#config-child-target

Parent config

locale
fr-FR
portal target
#config-parent-target

Nested config

locale
fr-FR
portal target
#config-child-target

The nested scope inherits fr-FR, but its Portal resolves to the nearer target override.

BitsConfig supplies scoped defaults to Bits UI descendants. A direct component prop has highest priority, followed by the nearest BitsConfig, inherited ancestor configs, and finally the built-in default. This lets one nested scope override defaultPortalTo while retaining an ancestor's defaultLocale.

defaultPortalTo accepts an Element or selector string; without a configured value, portals use document.body. defaultLocale falls back to en. Provider defaults affect only components inside the provider's subtree.