# OVERVIEW --- # Getting Started ## Quickstart Running tight on schedule? No worries! Check out our quickstart examples to get started with Ark UI in seconds. - [Next.js Template](https://stackblitz.com/edit/github-qcm2dskf) - [Solid Start Template](https://stackblitz.com/edit/github-1hgkbbln) - [Nuxt Template](https://stackblitz.com/edit/github-s3sg6syq) ## Setup Guide Before you start, ensure you have a proper project setup. If not, follow your preferred application framework setup guide and then return to this guide. Install the Ark UI dependency using your preferred package manager. ```bash npm install @ark-ui/solid // or pnpm install @ark-ui/solid // or yarn add @ark-ui/solid // or bun add @ark-ui/solid ``` In this guide, we will be adding a Slider component. Copy the following code to your project. ```tsx import { Slider } from '@ark-ui/solid/slider' import styles from 'styles/slider.module.css' export const Basic = () => { return (
Label
) } ```
Ark UI is a headless component library that doesn't include default styles. You can leverage the `data-scope` and `data-part` attributes to style your components with custom CSS. For example, to style a slider component, you can target its parts using these attributes: ```css /* Targets the */ [data-scope='slider'][data-part='root'] { display: flex; flex-direction: column; } ``` Check out the [Styling Components guide](/react/docs/guides/styling) to learn more about styling components in Ark UI. Congratulations! You've successfully set up and styled your components using Ark UI. If you run into any issues or have questions, open an issue on our [GitHub](https://github.com/chakra-ui/ark/issues/new/choose) or reach out on [Discord](https://discord.gg/ww6HE5xaZ2). Happy hacking! ✌️
# Changelog # @ark-ui/solid ## [5.39.2] - 2026-09-11 ### Fixed - **Listbox**: Fix `ItemContext.selected` staying `false` after selection. The option already set `data-selected` and `aria-selected`; the render-prop kept the first-render value. `ItemContext` is now an accessor, matching Select and Combobox. Read `item().selected` instead of `item.selected`. - **Field**: Fix `Field.Context` inside `Field.Item` missing later `invalid` and `disabled` changes on `Field.Root`. - **ColorPicker**: Fix `SwatchIndicator` inside `ValueSwatch` keeping the first-rendered color. - Fix `./hotkeys` and `./interaction` entrypoints missing from the published `exports` map. The build files shipped, but the publish config (`clean-package`) maintains its own `exports` map and was never updated when the `hotkeys` and `interaction` primitives were added, so `import { useHotkeys } from '@ark-ui/react/hotkeys'` failed with `ERR_MODULE_NOT_FOUND`. Both entrypoints are now included in the published map for every framework. - Fix the Solid build failing to resolve the `interaction` provider barrel. The providers index still imported `./interaction/index.ts` after the file was renamed to `index.tsx`, so `bun run build` aborted with an unresolved import. - Fix issue where the tour backdrop stayed visible after the tour was closed ## [5.39.1] - 2026-08-28 ### Fixed - Fix `NavigationMenu.Content` throwing `document is not defined` during SSR. ## [5.39.0] - 2026-08-21 ### Added - **Toc** [New]: Add a table of contents component that tracks which headings are in view as the reader scrolls. Pass the headings to `Toc.Root` as `items`, where each entry needs the heading element's `id` as `value` and its level as `depth`. Set `scrollEl` when the content scrolls inside a container rather than the page, so tracking observes that element instead of the viewport. ```tsx contentRef.current}> ``` More than one heading can be active at once, so `Toc.Item` carries `data-first` and `data-last` to mark the ends of the range, and `Toc.Indicator` spans it. Use `useToc` with `Toc.RootProvider` to reach `activeItems` and `scrollTo` from outside the tree. The API may still change while the component is in preview. - **Hotkeys** [New]: Add a `hotkeys` entrypoint with hooks for registering and inspecting keyboard shortcuts, built on `@zag-js/hotkeys`. `useHotkey` registers one command. `useHotkeys` registers several. `mod+K` resolves per platform, and sequences like `G > H` go through the same hook. Command ids are optional and generated when omitted. ```tsx useHotkey({ hotkey: 'mod+K', action: openSearch }) useHotkeys({ commands: [ { hotkey: 'mod+S', action: save, label: 'Save', category: 'File' }, { hotkey: 'G > H', action: goHome, label: 'Home' }, ], }) ``` `useHotkeyRegistrations` returns those commands with their metadata (`label`, `description`, `category`, `keywords`), so a command palette or shortcut dialog can render from the same registration that binds the key. Pass a store from `createHotkeyStore` to scope a set of commands. Without one, hooks share a default store. ```tsx const store = createHotkeyStore() useHotkeys({ commands, store }) const registered = useHotkeyRegistrations({ store }) ``` Active scopes, conflict behavior, sequence timeout, and default options are configured on the store. `usePressedKeys` and `useIsKeyPressed` track live key state. `useHotkeyRecorder` records a chord or sequence for rebinding UIs. `usePlatform` and `useFormatHotkey` render shortcuts for the current platform without a hydration mismatch. - **Presence**: Add `onEnterComplete`, called once the enter animation finishes, mirroring the existing `onExitComplete`. Vue exposes it as the `enter-complete` emit. > Affects Color Picker, Combobox, Date Picker, Dialog, Drawer, Floating Panel, Hover Card, Menu, Popover, Select, > Tooltip, and Tour. ### Fixed - Expose `ariaAttr` and `dataAttr` from the package root. ## [5.38.2] - 2026-08-17 ### Fixed - - **Date Picker** - Fix `translations` requiring every message. It's now `Partial`, so you can override one message and let the rest fall back to the defaults. - Fix the view trigger's `aria-label` naming the wrong view. In day view it announced "Switch to year view" while the trigger actually switches to month view. The trigger also disables itself once there's no further view to switch to. - Fix dates between a range's start and end announcing the generic "Choose" label. They now announce "In range". - **Escape Dismissal**: Fix `Escape` being ignored right after an overlay opens. Handlers registered a frame late, so the overlay was painted and focus-trapped before it could listen. Under CPU load that gap grew well past one frame and swallowed the keypress. They now register as soon as the layer mounts. > Affects Dialog, Drawer, Menu, Popover, and anything else that closes on `Escape`. - **Floating Panel** - Fix closing a panel leaving it on the stack, so the next panel now becomes topmost. - Fix stack order not applying to the positioner, so focusing a panel raises it above its siblings. - **Focus Visible**: Fix clicking a label adding `data-focus-visible` to the control. Activating the label briefly moved focus to an overlay container, which was read as virtual focus. > Affects Checkbox, Radio Group, and Switch. - **Form Submission**: Fix trigger and close trigger buttons submitting an ancestor form on click. They carried no `type`, so they defaulted to `type="submit"`. > Affects Drawer, Navigation Menu, Steps, and Tour. - **Hover Highlight**: Fix keyboard navigation losing or moving the highlighted item while the pointer rests over scrollable content. Scrolling the item into view moved the content under the cursor, and the resulting `pointerleave` (or `pointermove` in Safari) counted as a real hover. > Affects Cascade Select, Combobox, Listbox, Menu, and Select. - **Image Cropper**: Fix `fixedCropArea` disabling every keyboard interaction on the crop selection, including the `+` and `-` zoom shortcuts that still apply in fixed mode. The selection is now always focusable, and arrow keys pan the image since there's nothing to move or resize. - **QR Code**: Fix `getDataUrl()` and the download trigger dropping the overlay. The export contained only the QR matrix, so a logo or badge placed over the code went missing. - **Splitter**: Fix the resize trigger matching `:focus-visible` after a pointer drag. It still takes focus, so keyboard resizing keeps working, but no longer shows the focus ring. - **Tags Input**: Fix an XSS vector in the hidden element that measures input width. It set the tag value with `innerHTML`, so a value containing markup was parsed and could execute. It now uses `textContent`. ## [5.38.1] - 2026-08-07 ### Fixed - Fixed `DateInput.Segment` resolving segments by `type`, so segments sharing a type all rendered the first match's text. Literal separators like `:` and `,` rendered as `/`. ## [5.38.0] - 2026-08-01 ### Added - - **Number Input**: Add `largeStep` and `smallStep` props for configurable keyboard stepping. Hold `Shift` for `largeStep` (defaults to `10 * step`), `Alt` for `smallStep` (defaults to `step / 10`). The defaults match the previous behavior. ```jsx ``` - **Slider**: Add `largeStep` prop, applied on `Shift` or `PageUp`/`PageDown` (defaults to `10 * step`). The default matches the previous behavior. - - **Autofocus Control**: Add `data-autofocus` and `data-no-autofocus` to decide what gets focus when an overlay opens. Mark chrome like the close button with `data-no-autofocus` to skip it, or mark the real target with `data-autofocus`. ```jsx Close ``` Focus goes to `initialFocusEl`, then `[data-autofocus]`, then the first tabbable element without `[data-no-autofocus]`, then the content root. > Supported in Dialog and Drawer. - **Focus Trap**: Add `persistentElements` to treat portalled content as part of the trap when it isn't reachable via `aria-controls`/`aria-expanded`. Pass getters so the elements can be resolved lazily. ```jsx document.getElementById('toast-region')]} /> ``` - **Image Cropper**: `getCropData()` now returns the exact `corners` and `outputSize` of the crop in natural-image pixels, so you can hand the region straight to a server-side cropper. - **Image Cropper**: Add `maxSize` to `getCroppedImage()` to cap the output dimensions. The crop keeps its aspect ratio and scales down to fit. ```jsx const blob = await imageCropper.getCroppedImage({ maxSize: { width: 512, height: 512 } }) ``` ### Fixed - Exposed the toast content generic on `createToaster` so `CreateToasterReturn` can type custom toast data. - Fixed FloatingPanel `Content` and `Positioner` not reacting to presence changes. The panel never appeared when `lazyMount` was used, and was never removed from the DOM when `unmountOnExit` was used. `Content` now also forwards the presence ref so exit animations are tracked before unmounting. - - **Scroll Area**: Fixed `RootProvider` throwing `useScrollAreaContext returned undefined` by evaluating children outside the provider, and merge `getRootProps()` onto the root element. - **Password Input**: Fixed `RootProvider` spreading the machine `value` onto the root DOM element. - **Steps**: Fixed `RootProvider` rendering children twice via both `mergedProps` and explicit `{props.children}`. - **Marquee**: Fixed `Content` merging `children` into every cloned content element's props. - - **Date Input** - Type dates using your locale's native numerals (Arabic-Indic `٠-٩`, Devanagari `०-९`), not just ASCII digits. - Fix timezone-naive values (`CalendarDate`/`CalendarDateTime`) shifting by your local UTC offset when you pass a custom `formatter` without a `timeZone`. A wall-clock value now round-trips unchanged. - **Date Picker** - Type dates using your locale's native numerals, not just ASCII digits. - Reorder dates on blur in range selection, matching the other selection paths. - Fix the day view briefly flashing when you close the picker from the month or year view. - Fix `visibleRangeText` returning a stale value when multiple pickers share a visible range. This was also causing SSR hydration mismatches. - **Menu**: Fix the context menu flashing at the top-left before positioning. Long-press (touch) context menus no longer open stuck at `(0,0)`. - **Number Input** - Fix `api.setValue` throwing when you pass a number and `formatOptions` is set. - Fix `Cmd`/`Ctrl` + arrow keys producing values off the `step` grid. - **Slider**: Fix `Cmd`/`Ctrl` + arrow keys producing values off the `step` grid. - **Tags Input**: Fix native form submit so `FormData` reflects the current tags. The hidden input used to keep its initial value after you added, removed, or cleared tags. - **Toast**: Fix a height flicker when expanding the stack in overlap mode. Heights are now measured without the `scale` transform. - - **Color Picker**: Fix the channel input committing a partial value when you press `Enter` to confirm an IME composition. - **Date Input** - Fix segment text lagging behind while you type over an already committed date. - Fix in-progress edits being dropped while focus catches up after auto-advance. Fast typing and `ArrowUp`/`ArrowDown`/`Home`/`End` now land on the segment you're actually editing. - **Date Picker** - Fix disabled and read-only pickers still reacting to cell clicks, the clear trigger, and presets. Read-only pickers keep roving-focus navigation; disabled pickers drop out of the tab order. - Fix `minView`, `maxView`, and `defaultView` being ignored when resolving the initial view, which was hardcoded to day through year. - Fix `defaultOpen` winning over `open`, so a controlled picker could open against its own prop. - Fix `maxSelectedDates` not being enforced on month and year cells in `multiple` selection mode. - Fix keyboard range selection drifting from pointer behavior. Picking a third date restarts the range, and the hover preview updates for `Enter`, `Home`, `End`, and `PageUp`/`PageDown`. - Fix reopening the calendar with only a start date restarting the range instead of resuming it. - **Drawer**: Fix the backdrop flickering on a controlled close when the `open` setter is async. - **Field**: Fix `Field.Textarea` with `autoresize` dispatching a synthetic `input` event when you set `value` programmatically, which fed the value back into your framework and broke controlled state. - **Focus Return**: Fix three focus-trap issues you'd hit with overlays. - Closing an overlay no longer steals focus back from an element your app focused in the meantime, like a second dialog opened right after closing the first. - Closing a nested overlay, such as a popover inside a dialog, no longer throws when the outer container has no connected focusable element at that moment. - The focus ring now shows on the returned-to element after you close with `Escape`. > Affects Dialog, Drawer, Popover, and anything else that traps focus. - **Image Cropper**: Fix `getCroppedImage()` and `getCropData()` returning a different region from the one you see after rotating or flipping the image. - **Marquee**: Fix scroll speed depending on content width. The duration now comes from the content size and the actual translation distance, so `speed` matches real pixel speed even when the content is narrower than the viewport. - **Popover**: Fix tabbing out of portalled content looping back into the content when the trigger was the last tabbable element on the page. Focus now moves to the next tabbable element after the trigger. - **Scroll Lock**: Fix the scroll lock targeting `` on layouts where `` is the real scroll container, which meant nothing was locked while an overlay was open. - **Signature Pad**: Fix controlled `paths` drifting out of sync because the in-progress stroke was appended to `paths`. It now stays in `onDraw.currentPath` until the stroke ends. - **Splitter** - Fix collapsed panels sizing to `minSize` instead of `collapsedSize`. - Fix keyboard resizing breaking when a resize trigger got focus while hovered. - **Tour** - Fix dismissing a tour from a step's `effect` skipping cleanup, which could miss firing the `completed` status. - Fix a tooltip step's position resetting unexpectedly when the tour closed. - Fix a step action with `action: "skip"` doing nothing when clicked. - **Solid**: Fix a `value` of `null` being read as uncontrolled, so controlled components fell back to internal state. - **Solid, Svelte**: Fix machine exit actions running when a component was disposed before the machine started. - **Vue, Svelte**: Fix `defaultValue` being resolved before `value`, unlike React and Solid. ## [5.37.1] - 2026-06-06 ### Fixed - - **Date Input**: Fix segment placeholders for locales with explicit script subtags. - **Drawer**: Fix flickering when a controlled drawer is swiped or backdrop-closed while the `open` setter is async (e.g. the History API or a delayed state update). - **Image Cropper**: Fix `getCroppedImage` and `getCropData` returning the wrong region when the image is shown at a size different from its natural resolution (e.g. `width` / `height` of `100%`). - **Pin Input**: Fix `data-filled` being set on every input on first render. - **Signature Pad**: Fix the `dir` prop being accepted but never forwarded to the DOM. ## [5.37.0] - 2026-05-26 ### Added - **Floating Components**: Add `data-side` to placement-aware parts so you can style them based on the current placement (`top`, `bottom`, `left`, `right`). > Affects Color Picker, Combobox, Date Picker, Hover Card, Menu, Popover, Select, Tooltip, and Tour. - **Date Input**: Add `hideTimeZone` prop. When the value is a `ZonedDateTime`, the `timeZoneName` segment now renders automatically — set `hideTimeZone` to hide it. Arrow navigation and auto-advance after typing now reach read-only focusable segments too. - **Splitter** - Accept CSS units (`px`, `em`, `rem`, `vh`, `vw`) for `defaultSize`, `minSize`, and `maxSize` in addition to percentages. ```jsx ``` - Add `resizeBehavior` per panel. Set to `"preserve-pixel-size"` to keep a panel's pixel size constant when the parent splitter group resizes. - Allow non-panel children (toolbars, rails, status bars) inside the splitter root. Use partial trigger ids (`"left:"`, `":right"`) to bind handles around the fixed element. ### Fixed - **Accordion**: Remove redundant `aria-disabled` from item triggers. - **Color Picker**: Fire `onValueChangeEnd` when you pick a color with the EyeDropper API — matches the behavior when ending a drag on the area or channel sliders. - **Combobox**: Stop `Enter` from submitting the form when an item is highlighted, or when the typed value will be rejected by `allowCustomValue: false`. - **Date Input** - Preserve entered segments when applying min/max. Values clamp segment-by-segment on blur, so `06/15/1999` with min `2000-01-01` becomes `06/15/2000` instead of snapping to `01/01/2000`. - Fix range mode keyboard navigation so `ArrowRight` moves from the last segment of the start date to the first segment of the end date. - Fix time-only formatters (no `year` segment) never firing `onValueChange`. - Fix `setSegmentValue` reading stale display values. - Fix `dayPeriod` (AM/PM) arrow up/down not updating the visible segment when `hourCycle` changes at runtime. - Fix typing "A" / "P" on the `dayPeriod` segment not updating the visible AM/PM. - **Date Picker** - Fix clearing the value not resetting `activeIndex` and `hoveredValue` in range mode when input parts are not rendered. - Fix date input not being writable in locales with multi-character separators (e.g. `cs-CZ`, `sk-SK`, `hu-HU`, `ko-KR`). - Fix Firefox issue where the native month/year ` list().setFilterText(e.target.value)} /> {list().loading && ( Searching )} {list().error &&
Error: {list().error.message}
}
{(user) => (
{user.name}
{user.email}
{user.department} • {user.role}
)}
{list().items.length === 0 && !list().loading &&
No results found
} ) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` ### Sorting (Client-side) Sort data on the client side after loading from the server. ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { useCollator } from '@ark-ui/solid/locale' import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon, LoaderIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/async-list.module.css' interface User { id: number name: string username: string email: string phone: string website: string } export const SortClientSide = () => { const collator = useCollator() const list = useAsyncList({ autoReload: true, load: async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=5') const data = await response.json() return { items: data } }, sort({ items, descriptor }) { return { items: items.sort((a, b) => { const { column, direction } = descriptor let cmp = collator().compare(String(a[column]), String(b[column])) if (direction === 'descending') { cmp *= -1 } return cmp }), } }, }) const handleSort = (column: keyof User) => { const currentSort = list().sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list().sort({ column, direction }) } const getSortIcon = (column: keyof User) => { const current = list().sortDescriptor if (current?.column !== column) return return current.direction === 'ascending' ? : } const descriptor = () => list().sortDescriptor return (
{list().loading && (
Loading
)} {list().error &&
Error: {list().error.message}
}
Sorted by: {descriptor() ? `${descriptor()?.column} (${descriptor()?.direction})` : 'none'}
{({ key, label }) => ( )} {(user) => ( )}
handleSort(key as keyof User)}> {label} {getSortIcon(key as keyof User)}
{user.name} {user.username} {user.email}
) } ``` ### Sorting (Server-side) Send sort parameters to the server and reload data when sorting changes. ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon, LoaderIcon } from 'lucide-solid' import { For } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/async-list.module.css' interface Product { id: number title: string price: number description: string category: string image: string rating: { rate: number count: number } } export const SortServerSide = () => { const list = useAsyncList({ autoReload: true, async load({ sortDescriptor }) { const url = new URL('https://fakestoreapi.com/products') url.searchParams.set('limit', '5') if (sortDescriptor) { const { direction } = sortDescriptor url.searchParams.set('sort', direction === 'ascending' ? 'asc' : 'desc') } const response = await fetch(url) const items = await response.json() return { items } }, }) const handleSort = (column: keyof Product) => { const currentSort = list().sortDescriptor let direction: 'ascending' | 'descending' = 'ascending' if (currentSort?.column === column && currentSort.direction === 'ascending') { direction = 'descending' } list().sort({ column, direction }) } const getSortIcon = (column: keyof Product) => { const desc = list().sortDescriptor if (desc?.column !== column) return return desc.direction === 'ascending' ? : } return (
{list().loading && ( Loading )}
{list().error &&
Error: {list().error.message}
}
{(product) => (
{product.title}
{product.title}
${product.price}
{product.category} • {product.rating.rate} ({product.rating.count} reviews)
)}
) } ``` ### Dependencies Automatically reload data when dependencies change, such as filter selections or external state. ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { LoaderIcon } from 'lucide-solid' import { createSignal, For } from 'solid-js' import field from 'styles/field.module.css' import styles from 'styles/async-list.module.css' const LIMIT = 5 interface User { id: number name: string email: string department: string role: string } export const Dependencies = () => { const [selectedDepartment, setSelectedDepartment] = createSignal('') const [selectedRole, setSelectedRole] = createSignal('') const list = useAsyncList({ initialItems: mockUsers.slice(0, LIMIT), get dependencies() { return [selectedDepartment(), selectedRole()] }, async load({ filterText }: { filterText?: string }) { await delay(400) let items = mockUsers if (selectedDepartment()) { items = items.filter((user) => user.department === selectedDepartment()) } if (selectedRole()) { items = items.filter((user) => user.role === selectedRole()) } if (filterText) { items = items.filter( (user) => user.name.toLowerCase().includes(filterText.toLowerCase()) || user.email.toLowerCase().includes(filterText.toLowerCase()), ) } return { items: items.slice(0, LIMIT) } }, }) return (
list().setFilterText(e.target.value)} /> {list().loading && ( Loading )}
{list().error &&
Error: {list().error.message}
}
Found {list().items.length} users
{(user) => (
{user.name}
{user.email}
{user.department} • {user.role}
)}
{list().items.length === 0 && !list().loading && (
No users found with current filters
)}
) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const departments = ['Engineering', 'Marketing', 'Sales', 'Support'] const roles = [ 'Senior Developer', 'Marketing Manager', 'Frontend Developer', 'Sales Representative', 'DevOps Engineer', 'Customer Success', 'Content Creator', 'Backend Developer', 'Account Manager', 'Technical Support', 'Brand Manager', 'Full Stack Developer', 'Sales Director', 'Support Manager', 'UI Designer', 'Digital Marketer', 'Mobile Developer', 'Business Development', 'Documentation Specialist', 'Social Media Manager', ] const mockUsers: User[] = [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', role: 'Senior Developer' }, { id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', role: 'Marketing Manager' }, { id: 3, name: 'Carol Davis', email: 'carol@example.com', department: 'Engineering', role: 'Frontend Developer' }, { id: 4, name: 'David Wilson', email: 'david@example.com', department: 'Sales', role: 'Sales Representative' }, { id: 5, name: 'Eva Brown', email: 'eva@example.com', department: 'Engineering', role: 'DevOps Engineer' }, { id: 6, name: 'Frank Miller', email: 'frank@example.com', department: 'Support', role: 'Customer Success' }, { id: 7, name: 'Grace Lee', email: 'grace@example.com', department: 'Marketing', role: 'Content Creator' }, { id: 8, name: 'Henry Taylor', email: 'henry@example.com', department: 'Engineering', role: 'Backend Developer' }, { id: 9, name: 'Ivy Anderson', email: 'ivy@example.com', department: 'Sales', role: 'Account Manager' }, { id: 10, name: 'Jack Thompson', email: 'jack@example.com', department: 'Support', role: 'Technical Support' }, { id: 11, name: 'Kate Martinez', email: 'kate@example.com', department: 'Marketing', role: 'Brand Manager' }, { id: 12, name: 'Liam Garcia', email: 'liam@example.com', department: 'Engineering', role: 'Full Stack Developer' }, { id: 13, name: 'Mia Rodriguez', email: 'mia@example.com', department: 'Sales', role: 'Sales Director' }, { id: 14, name: 'Noah Lopez', email: 'noah@example.com', department: 'Support', role: 'Support Manager' }, { id: 15, name: 'Olivia White', email: 'olivia@example.com', department: 'Engineering', role: 'UI Designer' }, { id: 16, name: 'Paul Harris', email: 'paul@example.com', department: 'Marketing', role: 'Digital Marketer' }, { id: 17, name: 'Quinn Clark', email: 'quinn@example.com', department: 'Engineering', role: 'Mobile Developer' }, { id: 18, name: 'Ruby Lewis', email: 'ruby@example.com', department: 'Sales', role: 'Business Development' }, { id: 19, name: 'Sam Young', email: 'sam@example.com', department: 'Support', role: 'Documentation Specialist' }, { id: 20, name: 'Tina Walker', email: 'tina@example.com', department: 'Marketing', role: 'Social Media Manager' }, ] ``` ## API Reference ### Props - **load** (`(params: LoadParams) => Promise>`) - Function to load data asynchronously - **sort** (`(params: SortParams) => Promise> | SortResult`) - Optional function for client-side sorting - **autoReload** (`boolean`, default: `false`) - Whether to automatically reload data on mount - **initialItems** (`T[]`, default: `[]`) - Initial items to display before first load - **dependencies** (`any[]`, default: `[]`) - Values that trigger a reload when changed - **initialFilterText** (`string`, default: `''`) - Initial filter text value - **initialSortDescriptor** (`SortDescriptor | null`) - Initial sort configuration ### Load Parameters The `load` function receives an object with the following properties: - **cursor** (`C | undefined`) - Current cursor for pagination - **filterText** (`string`) - Current filter text - **sortDescriptor** (`SortDescriptor | null`) - Current sort configuration - **signal** (`AbortSignal`) - AbortController signal for request cancellation ### Load Result The `load` function should return an object with: - **items** (`T[]`) - The loaded items - **cursor** (`C | undefined`) - Optional cursor for next page ### Sort Parameters The `sort` function receives an object with: - **items** (`T[]`) - Current items to sort - **descriptor** (`SortDescriptor`) - Sort configuration with `column` and `direction` ### Return Value The hook returns an object with the following properties and methods: #### State Properties - **items** (`T[]`) - Current list of items - **loading** (`boolean`) - Whether a load operation is in progress - **error** (`Error | null`) - Any error from the last operation - **cursor** (`C | undefined`) - Current cursor for pagination - **filterText** (`string`) - Current filter text - **sortDescriptor** (`SortDescriptor | null`) - Current sort configuration #### Methods - **reload** (`() => void`) - Reload data from the beginning - **loadMore** (`() => void`) - Load more items (when cursor is available) - **setFilterText** (`(text: string) => void`) - Set filter text and reload - **sort** (`(descriptor: SortDescriptor) => void`) - Apply sorting #### Types ```tsx interface SortDescriptor { column: string direction: 'ascending' | 'descending' } interface LoadParams { cursor?: C filterText: string sortDescriptor?: SortDescriptor | null signal: AbortSignal } interface LoadResult { items: T[] cursor?: C } ``` # List Selection The `useListSelection` hook manages selection state in lists and collections. It supports single and multiple selection modes with operations like select, deselect, toggle, and clear. ```tsx import { createListCollection, useListSelection } from '@ark-ui/react/collection' const collection = createListCollection({ items: [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Cherry', value: 'cherry' }, ], }) const selection = useListSelection({ collection, selectionMode: 'single', deselectable: true, }) console.log(selection.selectedValues) // ['apple', 'banana', 'cherry'] ``` ## Examples ### Basic By default, the hook supports single selection mode that can be deselected. > Set `deselectable` to `false` to prevent deselecting the current selection. ```tsx import { createListCollection, useListSelection } from '@ark-ui/solid/collection' import { For } from 'solid-js' import styles from 'styles/list-selection.module.css' export const Basic = () => { const collection = createListCollection({ items: [ { label: 'React', value: 'react' }, { label: 'Vue', value: 'vue' }, { label: 'Angular', value: 'angular' }, { label: 'Svelte', value: 'svelte' }, ], }) const selection = useListSelection({ collection }) return (
Selected: {selection.selectedValues().join(', ') || 'None'} {(item) => ( )}
) } ``` ### Multiple Selection Set `selectionMode` to `multiple` to allow multiple items to be selected. ```tsx import { createListCollection, useListSelection } from '@ark-ui/solid/collection' import { For } from 'solid-js' import styles from 'styles/list-selection.module.css' export const Multiple = () => { const collection = createListCollection({ items: [ { label: 'React', value: 'react' }, { label: 'Vue', value: 'vue' }, { label: 'Angular', value: 'angular' }, { label: 'Svelte', value: 'svelte' }, { label: 'Solid', value: 'solid' }, ], }) const selection = useListSelection({ collection, selectionMode: 'multiple', }) const handleSelectAll = () => { if (selection.isAllSelected()) { selection.clear() } else { selection.setSelectedValues(collection.getValues()) } } return (
{selection.selectedValues().length} of {collection.items.length} selected
{(item) => ( )}
) } ``` ### Range Selection Here's an example of how to implement range selection that extends the selection from the first selected item to the clicked item. ```tsx import { createListCollection, useListSelection } from '@ark-ui/solid/collection' import { For } from 'solid-js' import styles from 'styles/list-selection.module.css' export const Range = () => { const collection = createListCollection({ items: [ { label: 'React', value: 'react' }, { label: 'Vue', value: 'vue' }, { label: 'Angular', value: 'angular' }, { label: 'Svelte', value: 'svelte' }, { label: 'Solid', value: 'solid' }, ], }) const selection = useListSelection({ collection, selectionMode: 'multiple', }) const handleItemClick = (value: string, event: MouseEvent) => { const firstSelectedValue = selection.firstSelectedValue() if (event.shiftKey && firstSelectedValue) { selection.extend(firstSelectedValue, value) } else if (event.ctrlKey || event.metaKey) { selection.toggle(value) } else { selection.replace(value) } } return (
Selected: {selection.selectedValues().join(', ') || 'None'} {(item) => ( )}

Click to select • Shift+Click for range • Cmd/Ctrl+Click to toggle

) } ``` ## API Reference ### Props - **collection** (`ListCollection`) - The collection to manage selection for - **selectionMode** (`'single' | 'multiple' | 'none'`, default: `'single'`) - The selection mode - **deselectable** (`boolean`, default: `true`) - Whether selected items can be deselected - **initialSelectedValues** (`string[]`, default: `[]`) - Initial selected values - **resetOnCollectionChange** (`boolean`, default: `false`) - Whether to reset selection when collection changes ### Return Value The hook returns an object with the following properties and methods: #### State Properties - **selectedValues** (`string[]`) - Array of currently selected values - **isEmpty** (`boolean`) - Whether no items are selected - **firstSelectedValue** (`string | null`) - The first selected value in collection order - **lastSelectedValue** (`string | null`) - The last selected value in collection order #### Query Methods - **isSelected** (`(value: string | null) => boolean`) - Check if a value is selected - **canSelect** (`(value: string) => boolean`) - Check if a value can be selected - **isAllSelected** (`() => boolean`) - Check if all items are selected - **isSomeSelected** (`() => boolean`) - Check if some items are selected #### Selection Methods - **select** (`(value: string, forceToggle?: boolean) => void`) - Select a value - **deselect** (`(value: string) => void`) - Deselect a value - **toggle** (`(value: string) => void`) - Toggle selection of a value - **replace** (`(value: string | null) => void`) - Replace selection with a single value - **extend** (`(anchorValue: string, targetValue: string) => void`) - Extend selection from anchor to target - **setSelectedValues** (`(values: string[]) => void`) - Set the selected values - **setSelection** (`(values: string[]) => void`) - Set the selection (alias for setSelectedValues) - **clear** (`() => void`) - Clear all selections - **resetSelection** (`() => void`) - Reset selection to initial state # COMPONENTS --- # Accordion ## Anatomy ```tsx ``` ## Examples ### Default Value Set the `defaultValue` prop to specify which item should be expanded by default. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const Basic = () => { return ( {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Controlled Use the `value` and `onValueChange` props to control the expanded items. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index, createSignal } from 'solid-js' import styles from 'styles/accordion.module.css' export const Controlled = () => { const [value, setValue] = createSignal([]) return ( setValue(details.value)}> {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Root Provider An alternative way to control the accordion is to use the `RootProvider` component and the `useAccordion` hook. This way you can access the state and methods from outside the component. ```tsx import { Accordion, useAccordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const RootProvider = () => { const accordion = useAccordion({ multiple: true, defaultValue: ['ark-ui'], }) return (
Value: {JSON.stringify(accordion().value)} {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Collapsible Use the `collapsible` prop to allow the user to collapse all panels. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const Collapsible = () => { return ( {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Multiple Use the `multiple` prop to allow multiple panels to be expanded simultaneously. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const Multiple = () => { return ( {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Horizontal By default, the Accordion is oriented vertically. Use the `orientation` prop to switch to a horizontal layout. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const Horizontal = () => { return ( {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Lazy Mount Use the `lazyMount` prop to defer rendering of accordion content until the item is expanded. Combine with `unmountOnExit` to unmount content when collapsed, freeing up resources. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const LazyMount = () => { return ( {(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Context Use `Accordion.Context` or `useAccordionContext` to access the accordion state. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const Context = () => { return ( {(context) => (
context.value: {JSON.stringify(context().value)}
context.focusedValue: {context().focusedValue || 'null'}
)}
{(item) => ( {item().title}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ### Item State Use `Accordion.ItemContext` or `useAccordionItemContext` to access the state of an accordion item. ```tsx import { Accordion } from '@ark-ui/solid/accordion' import { ChevronDownIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/accordion.module.css' export const ItemContext = () => { return ( {(item) => ( {item().title} {(context) => (
Expanded: {String(context().expanded)} Focused: {String(context().focused)} Disabled: {String(context().disabled)}
)}
{item().content}
)}
) } const items = [ { value: 'ark-ui', title: 'What is Ark UI?', content: 'A headless component library for building accessible web apps.', }, { value: 'getting-started', title: 'How to get started?', content: 'Install the package and import the components you need.', }, { value: 'maintainers', title: 'Who maintains this project?', content: 'Ark UI is maintained by the Chakra UI team.', }, ] ``` ## Guides ### Content Animation Use the `--height` and/or `--width` CSS variables to animate the size of the content when it expands or closes: ```css @keyframes slideDown { from { opacity: 0.01; height: 0; } to { opacity: 1; height: var(--height); } } @keyframes slideUp { from { opacity: 1; height: var(--height); } to { opacity: 0.01; height: 0; } } [data-scope='accordion'][data-part='item-content'][data-state='open'] { animation: slideDown 250ms ease-in-out; } [data-scope='accordion'][data-part='item-content'][data-state='closed'] { animation: slideUp 200ms ease-in-out; } ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`collapsible`** Type: `boolean` Required: false Default Value: `false` Description: Whether an accordion item can be closed after it has been expanded. **`defaultValue`** Type: `string[]` Required: false Default Value: `undefined` Description: The initial value of the expanded accordion items. Use when you don't need to control the value of the accordion. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the accordion items are disabled **`ids`** Type: `Partial<{ root: string item: (value: string) => string itemContent: (value: string) => string itemTrigger: (value: string) => string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the accordion. Useful for composition. **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`multiple`** Type: `boolean` Required: false Default Value: `false` Description: Whether multiple accordion items can be expanded at the same time. **`onFocusChange`** Type: `(details: FocusChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback fired when the focused accordion item changes. **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback fired when the state of expanded/collapsed accordion items changes. **`orientation`** Type: `'horizontal' | 'vertical'` Required: false Default Value: `"vertical"` Description: The orientation of the accordion items. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. **`value`** Type: `string[]` Required: false Default Value: `undefined` Description: The controlled value of the expanded accordion items. #### Data Attributes **`data-scope`**: accordion **`data-part`**: root **`data-orientation`**: The orientation of the accordion ### ItemContent #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: accordion **`data-part`**: item-content **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused **`data-orientation`**: The orientation of the item ### ItemIndicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: accordion **`data-part`**: item-indicator **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused **`data-orientation`**: The orientation of the item ### Item #### Props **`value`** Type: `string` Required: true Default Value: `undefined` Description: The value of the accordion item. **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the accordion item is disabled. #### Data Attributes **`data-scope`**: accordion **`data-part`**: item **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused **`data-disabled`**: Present when disabled **`data-orientation`**: The orientation of the item ### ItemTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: accordion **`data-part`**: item-trigger **`data-controls`**: **`data-orientation`**: The orientation of the item **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused ### RootProvider #### Props **`value`** Type: `UseAccordionReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `focusedValue` | `string | null` | The value of the focused accordion item. | | `value` | `string[]` | The value of the accordion | | `setValue` | `(value: string[]) => void` | Sets the value of the accordion | | `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of an accordion item. | ## Accessibility This component complies with the [Accordion WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/). ### Keyboard Support **`Space`** Description: When focus is on an trigger of a collapsed item, the item is expanded **`Enter`** Description: When focus is on an trigger of a collapsed section, expands the section. **`Tab`** Description: Moves focus to the next focusable element **`Shift + Tab`** Description: Moves focus to the previous focusable element **`ArrowDown`** Description: Moves focus to the next trigger **`ArrowUp`** Description: Moves focus to the previous trigger. **`Home`** Description: When focus is on an trigger, moves focus to the first trigger. **`End`** Description: When focus is on an trigger, moves focus to the last trigger. # Angle Slider ## Anatomy ```tsx ``` ## Examples ### Basic Here's a basic example of the Angle Slider component. ```tsx import { AngleSlider } from '@ark-ui/solid/angle-slider' import { For } from 'solid-js' import styles from 'styles/angle-slider.module.css' export const Basic = () => { return ( Rotation {(value) => } ) } ``` ### Controlled Use the `value` and `onValueChange` props to control the value of the Angle Slider. ```tsx import { AngleSlider } from '@ark-ui/solid/angle-slider' import { For, createSignal } from 'solid-js' import styles from 'styles/angle-slider.module.css' export const Controlled = () => { const [value, setValue] = createSignal(45) return ( setValue(e.value)}> Rotation {(value) => } ) } ``` ### Steps Use the `step` prop to set the discrete steps of the Angle Slider. ```tsx import { AngleSlider } from '@ark-ui/solid/angle-slider' import { For } from 'solid-js' import styles from 'styles/angle-slider.module.css' export const Step = () => { return ( 15 Step {(value) => } ) } ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`defaultValue`** Type: `number` Required: false Default Value: `0` Description: The initial value of the slider. Use when you don't need to control the value of the slider. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the slider is disabled. **`ids`** Type: `Partial<{ root: string thumb: string hiddenInput: string control: string valueText: string label: string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the machine. Useful for composition. **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the slider is invalid. **`name`** Type: `string` Required: false Default Value: `undefined` Description: The name of the slider. Useful for form submission. **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback function for when the value changes. **`onValueChangeEnd`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback function for when the value changes ends. **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the slider is read-only. **`step`** Type: `number` Required: false Default Value: `1` Description: The step value for the slider. **`value`** Type: `number` Required: false Default Value: `undefined` Description: The value of the slider. #### Data Attributes **`data-scope`**: angle-slider **`data-part`**: root **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: angle-slider **`data-part`**: control **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### HiddenInput #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: angle-slider **`data-part`**: label **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### MarkerGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Marker #### Props **`value`** Type: `number` Required: true Default Value: `undefined` Description: The value of the marker **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: angle-slider **`data-part`**: marker **`data-value`**: The value of the item **`data-state`**: **`data-disabled`**: Present when disabled ### RootProvider #### Props **`value`** Type: `UseAngleSliderReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Thumb #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: angle-slider **`data-part`**: thumb **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### ValueText #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `value` | `number` | The current value of the angle slider | | `valueAsDegree` | `string` | The current value as a degree string | | `setValue` | `(value: number) => void` | Sets the value of the angle slider | | `dragging` | `boolean` | Whether the slider is being dragged. | # Avatar ## Anatomy ```tsx ``` ## Examples ### Basic Display a user's profile image with a fallback. ```tsx import { Avatar } from '@ark-ui/solid/avatar' import styles from 'styles/avatar.module.css' export const Basic = () => ( PA ) ``` ### Events Use `onStatusChange` to listen for loading state changes. ```tsx import { Avatar } from '@ark-ui/solid/avatar' import { createSignal } from 'solid-js' import styles from 'styles/avatar.module.css' export const Events = () => { const [status, setStatus] = createSignal('loading...') return (
Status: {status()} setStatus(e.status)}> PA
) } ``` ### Root Provider An alternative way to control the avatar is to use the `RootProvider` component and the `useAvatar` hook. This way you can access the state and methods from outside the component. ```tsx import { Avatar, useAvatar } from '@ark-ui/solid/avatar' import { createSignal } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/avatar.module.css' export const RootProvider = () => { const [count, setCount] = createSignal(0) const avatar = useAvatar() return (
PA
) } ``` ## Guides ### Next.js Image Here's an example of how to use the `Image` component from `next/image`. ```tsx import { Avatar, useAvatarContext } from '@ark-ui/react/avatar' import { getImageProps, type ImageProps } from 'next/image' const AvatarNextImage = (props: ImageProps) => { const avatar = useAvatarContext() const { hidden, ...arkImageProps } = avatar.getImageProps() const nextImage = getImageProps(props) return ( ) } const Demo = () => { return ( JD ) } ``` > Refer to this [Github Discussion](https://github.com/chakra-ui/ark/discussions/3147) for more information. ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`ids`** Type: `Partial<{ root: string; image: string; fallback: string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the avatar. Useful for composition. **`onStatusChange`** Type: `(details: StatusChangeDetails) => void` Required: false Default Value: `undefined` Description: Functional called when the image loading status changes. ### Fallback #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: avatar **`data-part`**: fallback **`data-state`**: "hidden" | "visible" ### Image #### Props **`asChild`** Type: `(props: ParentProps<'img'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: avatar **`data-part`**: image **`data-state`**: "visible" | "hidden" ### RootProvider #### Props **`value`** Type: `UseAvatarReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `loaded` | `boolean` | Whether the image is loaded. | | `setSrc` | `(src: string) => void` | Function to set new src. | | `setLoaded` | `VoidFunction` | Function to set loaded state. | | `setError` | `VoidFunction` | Function to set error state. | # Carousel ## Anatomy ```tsx ``` ## Examples ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const Basic = () => { return ( {(image, index) => ( {image().alt} )} {(_, index) => } ) } ``` ### Controlled To create a controlled Carousel component, you can manage the state of the carousel using the `page` prop and update it when the `onPageChange` event handler is called: ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index, createSignal } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const Controlled = () => { const [page, setPage] = createSignal(0) return ( setPage(details.page)} > {(image, index) => ( {image().alt} )} {(_, index) => } ) } ``` ### Root Provider An alternative way to control the carousel is to use the `RootProvider` component and the `useCarousel` hook. This way you can access the state and methods from outside the component. ```tsx import { Carousel, useCarousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const RootProvider = () => { const carousel = useCarousel({ slideCount: images.length }) return (
Page: {carousel().page} {(image, index) => ( {image().alt} )} {(_, index) => }
) } ``` ### Autoplay Pass the `autoplay` and `loop` props to `Carousel.Root` to make the carousel play automatically. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ChevronLeftIcon, ChevronRightIcon, PauseIcon, PlayIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const Autoplay = () => { return ( {(image, index) => ( {image().alt} )} }> ) } ``` ### Pause on Hover This feature isn't built-in, but you can use the `play()` and `pause()` methods from `Carousel.Context` to implement pause on hover. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const PauseOnHover = () => { return ( {(carousel) => ( Autoplay is: {carousel().isPlaying ? 'playing' : 'paused'} )} {(api) => ( api().pause()} onPointerLeave={() => api().play()} > {(image, index) => ( {image().alt} )} )} {(_, index) => } ) } ``` ### Thumbnail Indicators Replace default indicator dots with image thumbnails. Render each thumbnail inside `Carousel.Indicator` to create a visual preview of each slide: ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const ThumbnailIndicator = () => { return ( {(image, index) => ( {image().alt} )} {(image, index) => ( {image().alt} )} ) } ``` ### Vertical Add the `orientation="vertical"` prop to `Carousel.Root` to switch the carousel to vertical scrolling. This can be helpful for displaying vertical galleries or content feeds. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowDownIcon, ArrowUpIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const images = [ { src: 'https://picsum.photos/seed/1/500/300', alt: 'Nature landscape' }, { src: 'https://picsum.photos/seed/2/500/300', alt: 'City skyline' }, { src: 'https://picsum.photos/seed/3/500/300', alt: 'Mountain view' }, { src: 'https://picsum.photos/seed/4/500/300', alt: 'Ocean sunset' }, { src: 'https://picsum.photos/seed/5/500/300', alt: 'Forest path' }, ] export const Vertical = () => { return ( {(image, index) => ( {image().alt} )} {(_, index) => } ) } ``` ### Dynamic Manage slides dynamically by storing them in state and syncing the carousel page. Pass the `page` prop and `onPageChange` handler to `Carousel.Root`, and update `slideCount` when slides are added or removed. This demonstrates bidirectional state synchronization between your component state and the carousel. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon, PlusIcon } from 'lucide-solid' import { Index, createSignal } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/carousel.module.css' export const DynamicSlides = () => { const [slides, setSlides] = createSignal([0, 1, 2, 3, 4]) const [page, setPage] = createSignal(0) const addSlide = () => { setSlides((prevSlides) => { const max = Math.max(...prevSlides) return [...prevSlides, max + 1] }) } return (
setPage(details.page)} > {(slide, index) => (
Slide {slide() + 1}
)}
{(_, index) => }
) } ``` ### Scroll to Slide Use `Carousel.Context` to access the carousel API and call `api.scrollToIndex(index)` to programmatically navigate to a specific slide. This is useful for creating custom navigation or jump-to-slide functionality. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/carousel.module.css' const slides = Array.from({ length: 6 }) export const ScrollTo = () => { return ( {(api) => ( )} {(_, index) => (
Slide {index + 1}
)}
{(_, index) => }
) } ``` ### Slides Per Page Display multiple slides simultaneously by setting the `slidesPerPage` prop on `Carousel.Root`. Use `api.pageSnapPoints` from `Carousel.Context` to render the correct number of indicators based on pages rather than individual slides. Add the `spacing` prop to control the gap between slides. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const slides = Array.from({ length: 6 }) export const SlidesPerPage = () => { return ( {(_, index) => (
Slide {index + 1}
)}
{(api) => ( {(_, index) => } )}
) } ``` ### Spacing Control the gap between slides using the `spacing` prop on `Carousel.Root`. Combine it with `slidesPerPage` to create layouts that show partial previews of adjacent slides. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/carousel.module.css' const slides = Array.from({ length: 6 }) export const Spacing = () => { return ( spacing='48px' {(_, index) => (
{index + 1}
)}
{(api) => ( {(_, index) => } )}
) } ``` ### Variable Sizes To allow slides with different widths, set the `autoSize` prop on `Carousel.Root`. This lets each `Carousel.Item` define its own width, and the carousel will adjust automatically. You can also use the `snapAlign` prop on individual items to control where each one snaps into view. ```tsx import { Carousel } from '@ark-ui/solid/carousel' import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/carousel.module.css' const items = [ { id: '1', width: '120px', label: 'Small' }, { id: '2', width: '200px', label: 'Medium Size' }, { id: '3', width: '80px', label: 'XS' }, { id: '4', width: '250px', label: 'Large Content Here' }, { id: '5', width: '150px', label: 'Regular' }, ] export const VariableSize = () => { return ( {(item, index) => (
{item.label}
)}
{(api) => ( {(_, index) => } )}
) } ``` ## API Reference ### Props ### Root #### Props **`slideCount`** Type: `number` Required: true Default Value: `undefined` Description: The total number of slides. Useful for SSR to render the initial ating the snap points. **`allowMouseDrag`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow scrolling via dragging with mouse **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`autoplay`** Type: `boolean | { delay: number }` Required: false Default Value: `false` Description: Whether to scroll automatically. The default delay is 4000ms. **`autoSize`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable variable width slides. **`defaultPage`** Type: `number` Required: false Default Value: `0` Description: The initial page to scroll to when rendered. Use when you don't need to control the page of the carousel. **`ids`** Type: `Partial<{ root: string item: (index: number) => string itemGroup: string nextTrigger: string prevTrigger: string indicatorGroup: string indicator: (index: number) => string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the carousel. Useful for composition. **`inViewThreshold`** Type: `number | number[]` Required: false Default Value: `0.6` Description: The threshold for determining if an item is in view. **`loop`** Type: `boolean` Required: false Default Value: `false` Description: Whether the carousel should loop around. **`onAutoplayStatusChange`** Type: `(details: AutoplayStatusDetails) => void` Required: false Default Value: `undefined` Description: Function called when the autoplay status changes. **`onDragStatusChange`** Type: `(details: DragStatusDetails) => void` Required: false Default Value: `undefined` Description: Function called when the drag status changes. **`onPageChange`** Type: `(details: PageChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the page changes. **`orientation`** Type: `'horizontal' | 'vertical'` Required: false Default Value: `"horizontal"` Description: The orientation of the element. **`padding`** Type: `string` Required: false Default Value: `undefined` Description: Defines the extra space added around the scrollable area, enabling nearby items to remain partially in view. **`page`** Type: `number` Required: false Default Value: `undefined` Description: The controlled page of the carousel. **`slidesPerMove`** Type: `number | 'auto'` Required: false Default Value: `"auto"` Description: The number of slides to scroll at a time. When set to `auto`, the number of slides to scroll is determined by the `slidesPerPage` property. **`slidesPerPage`** Type: `number` Required: false Default Value: `1` Description: The number of slides to show at a time. **`snapType`** Type: `'proximity' | 'mandatory'` Required: false Default Value: `"mandatory"` Description: The snap type of the item. **`spacing`** Type: `string` Required: false Default Value: `"0px"` Description: The amount of space between items. **`translations`** Type: `IntlTranslations` Required: false Default Value: `undefined` Description: The localized messages to use. #### Data Attributes **`data-scope`**: carousel **`data-part`**: root **`data-orientation`**: The orientation of the carousel ### AutoplayIndicator #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`fallback`** Type: `number | boolean | Node | ArrayElement | (string & {})` Required: false Default Value: `undefined` Description: The fallback content to render when autoplay is paused. ### AutoplayTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: autoplay-trigger **`data-orientation`**: The orientation of the autoplaytrigger **`data-pressed`**: Present when pressed ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: control **`data-orientation`**: The orientation of the control ### IndicatorGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: indicator-group **`data-orientation`**: The orientation of the indicatorgroup ### Indicator #### Props **`index`** Type: `number` Required: true Default Value: `undefined` Description: The index of the indicator. **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`readOnly`** Type: `boolean` Required: false Default Value: `false` Description: Whether the indicator is read only. #### Data Attributes **`data-scope`**: carousel **`data-part`**: indicator **`data-orientation`**: The orientation of the indicator **`data-index`**: The index of the item **`data-readonly`**: Present when read-only **`data-current`**: Present when current ### ItemGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: item-group **`data-orientation`**: The orientation of the item **`data-dragging`**: Present when in the dragging state ### Item #### Props **`index`** Type: `number` Required: true Default Value: `undefined` Description: The index of the item. **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`snapAlign`** Type: `'start' | 'end' | 'center'` Required: false Default Value: `"start"` Description: The snap alignment of the item. #### Data Attributes **`data-scope`**: carousel **`data-part`**: item **`data-index`**: The index of the item **`data-inview`**: Present when in viewport **`data-orientation`**: The orientation of the item ### NextTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: next-trigger **`data-orientation`**: The orientation of the nexttrigger ### PrevTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: carousel **`data-part`**: prev-trigger **`data-orientation`**: The orientation of the prevtrigger ### ProgressText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### RootProvider #### Props **`value`** Type: `UseCarouselReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `page` | `number` | The current index of the carousel | | `pageSnapPoints` | `number[]` | The current snap points of the carousel | | `isPlaying` | `boolean` | Whether the carousel is auto playing | | `isDragging` | `boolean` | Whether the carousel is being dragged. This only works when `draggable` is true. | | `canScrollNext` | `boolean` | Whether the carousel is can scroll to the next view | | `canScrollPrev` | `boolean` | Whether the carousel is can scroll to the previous view | | `scrollToIndex` | `(index: number, instant?: boolean) => void` | Function to scroll to a specific item index | | `scrollTo` | `(page: number, instant?: boolean) => void` | Function to scroll to a specific page | | `scrollNext` | `(instant?: boolean) => void` | Function to scroll to the next page | | `scrollPrev` | `(instant?: boolean) => void` | Function to scroll to the previous page | | `getProgress` | `() => number` | Returns the current scroll progress as a percentage | | `getProgressText` | `() => string` | Returns the progress text | | `play` | `VoidFunction` | Function to start/resume autoplay | | `pause` | `VoidFunction` | Function to pause autoplay | | `isInView` | `(index: number) => boolean` | Whether the item is in view | | `refresh` | `VoidFunction` | Function to re-compute the snap points and clamp the page | ## Accessibility Complies with the [Carousel WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/carousel/). # Checkbox ## Anatomy ```tsx ``` ## Examples ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' export const Basic = () => ( Checkbox ) ``` ### Default Checked Use the `defaultChecked` prop to set the initial checked state in an uncontrolled manner. The checkbox will manage its own state internally. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' export const DefaultChecked = () => ( Checkbox ) ``` ### Controlled Use the `checked` and `onCheckedChange` props to programatically control the checkbox's state. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/checkbox.module.css' export const Controlled = () => { const [checked, setChecked] = createSignal(true) return ( setChecked(e.checked)}> Checkbox ) } ``` ### Root Provider An alternative way to control the checkbox is to use the `RootProvider` component and the `useCheckbox` hook. This way you can access the state and methods from outside the component. ```tsx import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' import button from 'styles/button.module.css' export const RootProvider = () => { const checkbox = useCheckbox() return (
Checkbox {checkbox().checked ? ( ) : ( )}
) } ``` ### Disabled Use the `disabled` prop to make the checkbox non-interactive. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' export const Disabled = () => ( Checkbox ) ``` ### Indeterminate Use the `indeterminate` prop to create a checkbox in an indeterminate state (partially checked). ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon, MinusIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' export const Indeterminate = () => ( Checkbox ) ``` ### Field The checkbox integrates smoothly with the `Field` component to handle form state, helper text, and error text for proper accessibility. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { Field } from '@ark-ui/solid/field' import { CheckIcon, MinusIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' import field from 'styles/field.module.css' export const WithField = () => ( Label Additional Info Error Info ) ``` ### Form Pass the `name` and `value` props to the `Checkbox.Root` component to make the checkbox part of a form. The checkbox's value will be submitted with the form when the user submits it. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' import button from 'styles/button.module.css' export const WithForm = () => (
{ e.preventDefault() const formData = new FormData(e.currentTarget) console.log('terms:', formData.get('terms')) }} > I agree to the terms and conditions
) ``` ### Context Access the checkbox's state and methods with `Checkbox.Context` or the `useCheckboxContext` hook. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import styles from 'styles/checkbox.module.css' export const Context = () => ( {(checkbox) => Checked: {String(checkbox().checked)}} ) ``` ## Checkbox Group Use the `Checkbox.Group` component to manage a group of checkboxes. The `Checkbox.Group` component manages the state of the checkboxes and provides a way to access the checked values. ```tsx ``` ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' export const Group = () => ( {(item) => ( {item.label} )} ) const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Controlled Use the `value` and `onValueChange` props to programmatically control the checkbox group's state. This example demonstrates how to manage selected checkboxes in an array and display the current selection. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { createSignal, For } from 'solid-js' import styles from 'styles/checkbox.module.css' export const GroupControlled = () => { const [value, setValue] = createSignal(['react']) return (
value: {JSON.stringify(value())} {(item) => ( {item.label} )}
) } const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Root Provider Use the `useCheckboxGroup` hook to create the checkbox group store and pass it to the `Checkbox.GroupProvider` component. This provides maximum control over the group programmatically, similar to how `RootProvider` works for individual checkboxes. ```tsx import { Checkbox, useCheckboxGroup } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' export const GroupProvider = () => { const group = useCheckboxGroup({ defaultValue: ['react'], name: 'framework', }) return ( {(item) => ( {item.label} )} ) } const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Invalid Use the `invalid` prop on `Checkbox.Group` to mark the entire group as invalid for validation purposes. This applies the invalid state to all checkboxes within the group. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' export const GroupWithInvalid = () => ( {(item) => ( {item.label} )} ) const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Max Selected Use the `maxSelectedValues` prop to limit the number of checkboxes that can be selected at once. Once the maximum is reached, remaining checkboxes become disabled. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' export const GroupWithMaxSelected = () => ( {(item) => ( {item.label} )} ) const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, { label: 'Svelte', value: 'svelte' }, ] ``` ### Select All Implement a "select all" checkbox that controls all checkboxes within a group. The parent checkbox automatically shows an indeterminate state when some (but not all) items are selected, and becomes fully checked when all items are selected. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon, MinusIcon } from 'lucide-solid' import { For, createSignal, createMemo } from 'solid-js' import styles from 'styles/checkbox.module.css' const CheckboxItem = (props: Checkbox.RootProps) => ( {props.children} ) export const GroupWithSelectAll = () => { const [value, setValue] = createSignal([]) const handleSelectAll = (checked: boolean) => { setValue(checked ? items.map((item) => item.value) : []) } const allSelected = createMemo(() => value().length === items.length) const indeterminate = createMemo(() => value().length > 0 && value().length < items.length) return (
Selected: {JSON.stringify(value())} handleSelectAll(!!details.checked)} > JSX Frameworks {(item) => {item.label}}
) } const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Form Use the `Checkbox.Group` component within a form to handle multiple checkbox values with form submission. The `name` prop ensures all selected values are collected as an array when the form is submitted using `FormData.getAll()`. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' import button from 'styles/button.module.css' export const GroupWithForm = () => (
{ e.preventDefault() console.log(new FormData(e.currentTarget).getAll('framework')) }} > {(item) => ( {item.label} )}
) const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ### Fieldset Use the `Fieldset` component with `Checkbox.Group` to provide semantic grouping with legend, helper text, and error text support. ```tsx import { Checkbox } from '@ark-ui/solid/checkbox' import { Fieldset } from '@ark-ui/solid/fieldset' import { CheckIcon } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/checkbox.module.css' import fieldset from 'styles/fieldset.module.css' export const GroupWithFieldset = () => ( Select frameworks Choose your preferred frameworks {(item) => ( {item.label} )} ) const items = [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, ] ``` ## Guides ### asChild Behavior The `Checkbox.Root` element of the checkbox is a `label` element. This is because the checkbox is a form control and should be associated with a label to provide context and meaning to the user. Otherwise, the HTML and accessibility structure will be invalid. > If you need to use the `asChild` property, make sure that the `label` element is the direct child of the > `Checkbox.Root` component. ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`checked`** Type: `CheckedState` Required: false Default Value: `undefined` Description: The controlled checked state of the checkbox **`defaultChecked`** Type: `CheckedState` Required: false Default Value: `undefined` Description: The initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the checkbox is disabled **`form`** Type: `string` Required: false Default Value: `undefined` Description: The id of the form that the checkbox belongs to. **`ids`** Type: `Partial<{ root: string; hiddenInput: string; control: string; label: string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the checkbox. Useful for composition. **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the checkbox is invalid **`name`** Type: `string` Required: false Default Value: `undefined` Description: The name of the input field in a checkbox. Useful for form submission. **`onCheckedChange`** Type: `(details: CheckedChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback invoked when the checked state changes. **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the checkbox is read-only **`required`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the checkbox is required **`value`** Type: `string` Required: false Default Value: `"on"` Description: The value of checkbox input. Useful for form submission. #### Data Attributes **`data-active`**: Present when active or pressed **`data-focus`**: Present when focused **`data-focus-visible`**: Present when focused with keyboard **`data-readonly`**: Present when read-only **`data-hover`**: Present when hovered **`data-disabled`**: Present when disabled **`data-state`**: "indeterminate" | "checked" | "unchecked" **`data-invalid`**: Present when invalid **`data-required`**: Present when required ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-active`**: Present when active or pressed **`data-focus`**: Present when focused **`data-focus-visible`**: Present when focused with keyboard **`data-readonly`**: Present when read-only **`data-hover`**: Present when hovered **`data-disabled`**: Present when disabled **`data-state`**: "indeterminate" | "checked" | "unchecked" **`data-invalid`**: Present when invalid **`data-required`**: Present when required ### Group #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`defaultValue`** Type: `string[] | Accessor` Required: false Default Value: `undefined` Description: The initial value of `value` when uncontrolled **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: If `true`, the checkbox group is disabled **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: If `true`, the checkbox group is invalid **`maxSelectedValues`** Type: `number` Required: false Default Value: `undefined` Description: The maximum number of selected values **`name`** Type: `string` Required: false Default Value: `undefined` Description: The name of the input fields in the checkbox group (Useful for form submission). **`onValueChange`** Type: `(value: string[]) => void` Required: false Default Value: `undefined` Description: The callback to call when the value changes **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: If `true`, the checkbox group is read-only **`value`** Type: `Accessor` Required: false Default Value: `undefined` Description: The controlled value of the checkbox group ### GroupProvider #### Props **`value`** Type: `UseCheckboxGroupContext` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### HiddenInput #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Indicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`indeterminate`** Type: `boolean` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-active`**: Present when active or pressed **`data-focus`**: Present when focused **`data-focus-visible`**: Present when focused with keyboard **`data-readonly`**: Present when read-only **`data-hover`**: Present when hovered **`data-disabled`**: Present when disabled **`data-state`**: "indeterminate" | "checked" | "unchecked" **`data-invalid`**: Present when invalid **`data-required`**: Present when required ### Label #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-active`**: Present when active or pressed **`data-focus`**: Present when focused **`data-focus-visible`**: Present when focused with keyboard **`data-readonly`**: Present when read-only **`data-hover`**: Present when hovered **`data-disabled`**: Present when disabled **`data-state`**: "indeterminate" | "checked" | "unchecked" **`data-invalid`**: Present when invalid **`data-required`**: Present when required ### RootProvider #### Props **`value`** Type: `UseCheckboxReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `checked` | `boolean` | Whether the checkbox is checked | | `disabled` | `boolean | undefined` | Whether the checkbox is disabled | | `indeterminate` | `boolean` | Whether the checkbox is indeterminate | | `focused` | `boolean | undefined` | Whether the checkbox is focused | | `checkedState` | `CheckedState` | The checked state of the checkbox | | `setChecked` | `(checked: CheckedState) => void` | Function to set the checked state of the checkbox | | `toggleChecked` | `VoidFunction` | Function to toggle the checked state of the checkbox | ## Accessibility Complies with the [Checkbox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/). ### Keyboard Support **`Space`** Description: Toggle the checkbox # Clipboard ## Anatomy ```tsx ``` ## Examples ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import styles from 'styles/clipboard.module.css' export const Basic = () => { return ( Copy this link }> ) } ``` ### Controlled Control the clipboard value externally by managing the state yourself and using `onValueChange` to handle updates. ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/clipboard.module.css' export const Controlled = () => { const [url, setUrl] = createSignal('https://ark-ui.com') return (
setUrl(details.value)}> Copy this link }>
) } ``` ### Root Provider An alternative way to control the clipboard is to use the `RootProvider` component and the `useClipboard` hook. This way you can access the state and methods from outside the component. ```tsx import { Clipboard, useClipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import styles from 'styles/clipboard.module.css' export const RootProvider = () => { const clipboard = useClipboard({ value: 'https://ark-ui.com' }) return (
value: {clipboard().value}, copied: {String(clipboard().copied)} Copy this link }>
) } ``` ### Context Access the clipboard's state with `Clipboard.Context` or the `useClipboardContext` hook. You get properties like `copied`, `value`, and `setValue`. > Alternatively, you can use the `useClipboardContext` hook to access the clipboard context. ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import { Show } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/clipboard.module.css' export const Context = () => { return ( Copy this link {(clipboard) => ( )} ) } ``` ### Copy Status Use the `onStatusChange` prop to listen for copy operations. It exposes a `copied` property that you can use to display a success message. ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/clipboard.module.css' export const CopyStatus = () => { const [copyCount, setCopyCount] = createSignal(0) return ( { if (details.copied) { setCopyCount((prev) => prev + 1) } }} > }>

Copied {copyCount()} times

) } ``` ### Timeout Configure the copy status timeout duration using the `timeout` prop. Default is 3000ms (3 seconds). ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import styles from 'styles/clipboard.module.css' export const Timeout = () => { return ( Copy this link (5 second timeout) }> ) } ``` ### Value Text Use `Clipboard.ValueText` to display the current clipboard value. ```tsx import { Clipboard } from '@ark-ui/solid/clipboard' import { CheckIcon, ClipboardCopyIcon } from 'lucide-solid' import styles from 'styles/clipboard.module.css' export const ValueText = () => { return ( }> ) } ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`defaultValue`** Type: `string` Required: false Default Value: `undefined` Description: The initial value to be copied to the clipboard when rendered. Use when you don't need to control the value of the clipboard. **`ids`** Type: `Partial<{ root: string; input: string; label: string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the clipboard. Useful for composition. **`onStatusChange`** Type: `(details: CopyStatusDetails) => void` Required: false Default Value: `undefined` Description: The function to be called when the value is copied to the clipboard **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: The function to be called when the value changes **`timeout`** Type: `number` Required: false Default Value: `3000` Description: The timeout for the copy operation **`translations`** Type: `IntlTranslations` Required: false Default Value: `undefined` Description: Specifies the localized strings that identifies the accessibility elements and their states **`value`** Type: `string` Required: false Default Value: `undefined` Description: The controlled value of the clipboard #### Data Attributes **`data-scope`**: clipboard **`data-part`**: root **`data-copied`**: Present when copied state is true ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: clipboard **`data-part`**: control **`data-copied`**: Present when copied state is true ### Indicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`copied`** Type: `number | boolean | Node | ArrayElement | (string & {})` Required: false Default Value: `undefined` Description: undefined ### Input #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: clipboard **`data-part`**: input **`data-copied`**: Present when copied state is true **`data-readonly`**: Present when read-only ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: clipboard **`data-part`**: label **`data-copied`**: Present when copied state is true ### RootProvider #### Props **`value`** Type: `UseClipboardReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Trigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: clipboard **`data-part`**: trigger **`data-copied`**: Present when copied state is true ### ValueText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `copied` | `boolean` | Whether the value has been copied to the clipboard | | `value` | `string` | The value to be copied to the clipboard | | `setValue` | `(value: string) => void` | Set the value to be copied to the clipboard | | `copy` | `VoidFunction` | Copy the value to the clipboard | # Collapsible ## Anatomy ```tsx ``` ## Examples ```tsx import { Collapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const Basic = () => ( What is Ark UI?
Ark UI is a headless component library for building accessible, high-quality UI components for React, Solid, Vue, and Svelte.
) ``` ### Disabled Use the `disabled` prop to disable the collapsible and prevent it from being toggled. ```tsx import { Collapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const Disabled = () => ( System Requirements
This section is currently unavailable.
) ``` ### Partial Collapse Use the `collapsedHeight` or `collapsedWidth` props to create a "show more/less" pattern. When set, the content maintains the specified dimensions when collapsed instead of collapsing to 0px. We expose the `--collapsed-height` or `--collapsed-width` variables to use in your CSS animations. ```tsx import { Collapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const PartialCollapse = () => ( Read More

Ark UI is a headless component library for building accessible, high-quality UI components for React, Solid, Vue, and Svelte. It provides unstyled, fully accessible components that you can customize to match your design system.

Built on top of Zag.js state machines, Ark UI ensures consistent behavior across all frameworks while giving you complete control over styling. Each component follows WAI-ARIA patterns for accessibility out of the box.

Whether you're building a design system from scratch or need reliable primitives for your next project, Ark UI provides the foundation you need without imposing any visual constraints.

) ``` > Interactive elements (links, buttons, inputs) within the collapsed area automatically become `inert` to prevent > keyboard navigation to hidden content. ### Nested Collapsibles You can nest collapsibles within collapsibles to create hierarchical content structures. ```tsx import { Collapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const Nested = () => ( Getting Started

Welcome to the Ark UI documentation. Here are some topics to explore:

Installation

Install Ark UI using your preferred package manager:

npm install @ark-ui/solid
Styling

Ark UI components are unstyled by default. Use CSS modules, Tailwind, or any styling solution.

) ``` ### Lazy Mount Use `lazyMount` to delay mounting the content until first opened, and `unmountOnExit` to remove it from the DOM when collapsed. Combining both ensures the component is only in the DOM while expanded. ```tsx import { Collapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const LazyMount = () => ( Session Details
This content is lazily mounted when first opened and removed from the DOM when collapsed.
) ``` ### Root Provider An alternative way to control the collapsible is to use the `RootProvider` component and the `useCollapsible` hook. This way you can access the state and methods from outside the component. ```tsx import { Collapsible, useCollapsible } from '@ark-ui/solid/collapsible' import { ChevronRightIcon } from 'lucide-solid' import styles from 'styles/collapsible.module.css' export const RootProvider = () => { const collapsible = useCollapsible() return (
open: {String(collapsible().open)}, visible: {String(collapsible().visible)} Toggle Panel
This panel can be toggled by the button above, which uses the useCollapsible hook for state management.
) } ``` ## Guides ### Indicator Animation To rotate the indicator icon (such as a chevron) when the collapsible opens and closes, use CSS transforms with the `data-state` attribute: ```css [data-scope='collapsible'][data-part='indicator'] { transition: transform 200ms; &[data-state='open'] { transform: rotate(180deg); } } ``` ### Open vs Visible When using `useCollapsible` or `useCollapsibleContext`, you can access the `open` and `visible` state properties. They seem similar but serve different purposes: - **`open`**: Indicates the intended state of the collapsible. This is `true` when the collapsible should be expanded and `false` when it should be collapsed. This changes immediately when triggered. - **`visible`**: Indicates whether the content is currently visible in the DOM. This accounts for exit animations - the content remains `visible` while the closing animation plays, even though `open` is already `false`. Once the animation completes, `visible` becomes `false`. ### Content Animation Use the `--height` and/or `--width` CSS variables to animate the size of the content when it expands or closes. If you use `collapsedHeight` or `collapsedWidth`, update your CSS animations to use the `--collapsed-height` or `--collapsed-width` variables as the starting/ending point: ```css @keyframes expand { from { height: var(--collapsed-height, 0); } to { height: var(--height); } } @keyframes collapse { from { height: var(--height); } to { height: var(--collapsed-height, 0); } } [data-scope='collapsible'][data-part='content'] { &[data-state='open'] { animation: expand 250ms; } &[data-state='closed'] { animation: collapse 250ms; } } ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`collapsedHeight`** Type: `string | number` Required: false Default Value: `undefined` Description: The height of the content when collapsed. **`collapsedWidth`** Type: `string | number` Required: false Default Value: `undefined` Description: The width of the content when collapsed. **`defaultOpen`** Type: `boolean` Required: false Default Value: `undefined` Description: The initial open state of the collapsible when rendered. Use when you don't need to control the open state of the collapsible. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the collapsible is disabled. **`ids`** Type: `Partial<{ root: string; content: string; trigger: string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the collapsible. Useful for composition. **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: The callback invoked when the exit animation completes. **`onOpenChange`** Type: `(details: OpenChangeDetails) => void` Required: false Default Value: `undefined` Description: The callback invoked when the open state changes. **`open`** Type: `boolean` Required: false Default Value: `undefined` Description: The controlled open state of the collapsible. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. #### Data Attributes **`data-scope`**: collapsible **`data-part`**: root **`data-state`**: "open" | "closed" ### Content #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: collapsible **`data-part`**: content **`data-collapsible`**: **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-has-collapsed-size`**: Present when the content has collapsed width or height ### Indicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: collapsible **`data-part`**: indicator **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled ### RootProvider #### Props **`value`** Type: `UseCollapsibleReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Trigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: collapsible **`data-part`**: trigger **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `open` | `boolean` | Whether the collapsible is open. | | `visible` | `boolean` | Whether the collapsible is visible (open or closing) | | `disabled` | `boolean` | Whether the collapsible is disabled | | `setOpen` | `(open: boolean) => void` | Function to open or close the collapsible. | | `measureSize` | `VoidFunction` | Function to measure the size of the content. | ## Accessibility ### Keyboard Support **`Space`** Description: Opens/closes the collapsible. **`Enter`** Description: Opens/closes the collapsible. # Color Picker ## Anatomy ```tsx ``` ## Examples ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Pipette } from 'lucide-solid' import styles from 'styles/color-picker.module.css' export const Basic = () => { return ( Color
) } ``` ### Controlled Use the `value` and `onValueChange` props to programatically control the color picker's state. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Pipette } from 'lucide-solid' import { createSignal } from 'solid-js' import styles from 'styles/color-picker.module.css' export const Controlled = () => { const [color, setColor] = createSignal(parseColor('hsl(20, 100%, 50%)')) return (
Selected color: {color().toString('hex')} setColor(e.value)}> Color
) } ``` ### Open Controlled Control the open state of the color picker popover programmatically using the `open` and `onOpenChange` props. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Pipette } from 'lucide-solid' import { createSignal } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/color-picker.module.css' export const OpenControlled = () => { const [open, setOpen] = createSignal(false) return (
setOpen(e.open)} defaultValue={parseColor('#eb5e41')} > Color
) } ``` ### Root Provider An alternative way to control the color picker is to use the `RootProvider` component and the `useColorPicker` hook. This way you can access the state and methods from outside the component. ```tsx import { ColorPicker, parseColor, useColorPicker } from '@ark-ui/solid/color-picker' import { Check, Pipette } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/color-picker.module.css' const swatches = ['red', 'blue', 'green', 'orange'] export const RootProvider = () => { const colorPicker = useColorPicker({ defaultValue: parseColor('#eb5e41') }) return (
Color: {colorPicker().valueAsString} Color
{(color) => ( )}
) } ``` ### Disabled Use the `disabled` prop to disable the color picker. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import styles from 'styles/color-picker.module.css' export const Disabled = () => { return ( Color
) } ``` ### Inline Render the color picker inline without a popover by using the `inline` prop. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Check } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/color-picker.module.css' const swatches = ['red', 'blue', 'green', 'orange'] export const Inline = () => { return ( {(color) => ( )} ) } ``` ### Input Only A minimal color picker with just an input field, value swatch, and eye dropper trigger. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Pipette } from 'lucide-solid' import styles from 'styles/color-picker.module.css' export const InputOnly = () => { return ( Color ) } ``` ### Slider Only Display only the channel sliders for RGB color selection. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import styles from 'styles/color-picker.module.css' export const SliderOnly = () => { return (
R
G
B
) } ``` ### Swatch Only A simple color picker with only preset color swatches. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Check } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/color-picker.module.css' const swatches = ['red', 'pink', 'orange', 'purple'] export const SwatchOnly = () => { return ( Selected color: {(color) => ( )} ) } ``` ### Swatches Include preset color swatches in the color picker content for quick color selection. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Check, Pipette } from 'lucide-solid' import { For } from 'solid-js' import styles from 'styles/color-picker.module.css' const swatches = ['red', 'blue', 'green', 'orange'] export const Swatches = () => { return ( Color
{(color) => ( )}
) } ``` ### Value Swatch Display the current color value as a swatch alongside the color area and sliders. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import styles from 'styles/color-picker.module.css' export const ValueSwatch = () => { return (
) } ``` ### Field The `Field` component helps manage form-related state and accessibility attributes of a color picker. It includes handling ARIA labels, helper text, and error text to ensure proper accessibility. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Field } from '@ark-ui/solid/field' import field from 'styles/field.module.css' import styles from 'styles/color-picker.module.css' export const WithField = () => ( Label
Additional Info Error Info
) ``` ### Form Usage Integrate the color picker with form libraries like React Hook Form using the `HiddenInput` component. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { createForm, setValue } from '@modular-forms/solid' import { Pipette } from 'lucide-solid' import button from 'styles/button.module.css' import styles from 'styles/color-picker.module.css' export const FormUsage = () => { const [formStore, { Form, Field }] = createForm<{ color: string }>() return (
{ window.alert(JSON.stringify(data)) }} > {(field) => ( setValue(formStore, 'color', details.valueAsString)} >
)}
) } ``` ### Inside Dialog Here's an example of how to use the color picker inside a dialog. ```tsx import { ColorPicker, parseColor } from '@ark-ui/solid/color-picker' import { Dialog } from '@ark-ui/solid/dialog' import { Pipette, XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import dialog from 'styles/dialog.module.css' import styles from 'styles/color-picker.module.css' export const InsideDialog = () => ( Open Dialog Choose a color Wrap the positioner in a portal so the popover appears above the dialog.
Color
) ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`closeOnSelect`** Type: `boolean` Required: false Default Value: `false` Description: Whether to close the color picker when a swatch is selected **`defaultFormat`** Type: `ColorFormat` Required: false Default Value: `"rgba"` Description: The initial color format when rendered. Use when you don't need to control the color format of the color picker. **`defaultOpen`** Type: `boolean` Required: false Default Value: `undefined` Description: The initial open state of the color picker when rendered. Use when you don't need to control the open state of the color picker. **`defaultValue`** Type: `Color` Required: false Default Value: `#000000` Description: The initial color value when rendered. Use when you don't need to control the color value of the color picker. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the color picker is disabled **`format`** Type: `ColorFormat` Required: false Default Value: `undefined` Description: The controlled color format to use **`ids`** Type: `Partial<{ root: string; control: string; trigger: string; label: string; input: string; hiddenInput: string; content: string; area: string; areaGradient: string; positioner: string; formatSelect: string; areaThumb: string; channelInput: (id: string) => string; channelSliderTrack: (id: ColorChannel) => string; channe...` Required: false Default Value: `undefined` Description: The ids of the elements in the color picker. Useful for composition. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`initialFocusEl`** Type: `() => HTMLElement | null` Required: false Default Value: `undefined` Description: The initial focus element when the color picker is opened. **`inline`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to render the color picker inline **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the color picker is invalid **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`name`** Type: `string` Required: false Default Value: `undefined` Description: The name for the form input **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`onFocusOutside`** Type: `(event: FocusOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when the focus is moved outside the component **`onFormatChange`** Type: `(details: FormatChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the color format changes **`onInteractOutside`** Type: `(event: InteractOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when an interaction happens outside the component **`onOpenChange`** Type: `(details: OpenChangeDetails) => void` Required: false Default Value: `undefined` Description: Handler that is called when the user opens or closes the color picker. **`onPointerDownOutside`** Type: `(event: PointerDownOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when the pointer is pressed down outside the component **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Handler that is called when the value changes, as the user drags. **`onValueChangeEnd`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Handler that is called when the user stops dragging. **`open`** Type: `boolean` Required: false Default Value: `undefined` Description: The controlled open state of the color picker **`openAutoFocus`** Type: `boolean` Required: false Default Value: `true` Description: Whether to auto focus the color picker when it is opened **`positioning`** Type: `PositioningOptions` Required: false Default Value: `undefined` Description: The positioning options for the color picker **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the color picker is read-only **`required`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the color picker is required **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. **`value`** Type: `Color` Required: false Default Value: `undefined` Description: The controlled color value of the color picker #### Data Attributes **`data-scope`**: color-picker **`data-part`**: root **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid ### AreaBackground #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: area-background **`data-invalid`**: Present when invalid **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only ### Area #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`xChannel`** Type: `ColorChannel` Required: false Default Value: `undefined` Description: undefined **`yChannel`** Type: `ColorChannel` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: color-picker **`data-part`**: area **`data-invalid`**: Present when invalid **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only ### AreaThumb #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: area-thumb **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### ChannelInput #### Props **`channel`** Type: `ExtendedColorChannel` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`orientation`** Type: `Orientation` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-input **`data-channel`**: The color channel of the channelinput **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### ChannelSliderLabel #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-slider-label **`data-channel`**: The color channel of the channelsliderlabel ### ChannelSlider #### Props **`channel`** Type: `ColorChannel` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`orientation`** Type: `Orientation` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-slider **`data-channel`**: The color channel of the channelslider **`data-orientation`**: The orientation of the channelslider ### ChannelSliderThumb #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-slider-thumb **`data-channel`**: The color channel of the channelsliderthumb **`data-disabled`**: Present when disabled **`data-orientation`**: The orientation of the channelsliderthumb ### ChannelSliderTrack #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-slider-track **`data-channel`**: The color channel of the channelslidertrack **`data-orientation`**: The orientation of the channelslidertrack ### ChannelSliderValueText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: channel-slider-value-text **`data-channel`**: The color channel of the channelslidervaluetext ### Content #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: content **`data-placement`**: The placement of the content **`data-nested`**: popover **`data-has-nested`**: popover **`data-side`**: The side of the trigger that the content is positioned on **`data-state`**: "open" | "closed" ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: control **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused ### EyeDropperTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: eye-dropper-trigger **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### FormatSelect #### Props **`asChild`** Type: `(props: ParentProps<'select'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### FormatTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### HiddenInput #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: label **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-required`**: Present when required **`data-focus`**: Present when focused ### Positioner #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### RootProvider #### Props **`value`** Type: `UseColorPickerReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. ### SwatchGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### SwatchIndicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Swatch #### Props **`value`** Type: `string | Color` Required: true Default Value: `undefined` Description: The color value **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`respectAlpha`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to include the alpha channel in the color #### Data Attributes **`data-scope`**: color-picker **`data-part`**: swatch **`data-state`**: "checked" | "unchecked" **`data-value`**: The value of the item ### SwatchTrigger #### Props **`value`** Type: `string | Color` Required: true Default Value: `undefined` Description: The color value **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the swatch trigger is disabled #### Data Attributes **`data-scope`**: color-picker **`data-part`**: swatch-trigger **`data-state`**: "checked" | "unchecked" **`data-value`**: The value of the item **`data-disabled`**: Present when disabled ### TransparencyGrid #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`size`** Type: `string` Required: false Default Value: `undefined` Description: undefined ### Trigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: color-picker **`data-part`**: trigger **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-placement`**: The placement of the trigger **`data-side`**: The side of the trigger that the trigger is positioned on **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused ### ValueSwatch #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`respectAlpha`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to include the alpha channel in the color ### ValueText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`format`** Type: `ColorStringFormat` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: color-picker **`data-part`**: value-text **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused ### View #### Props **`format`** Type: `ColorFormat` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `dragging` | `boolean` | Whether the color picker is being dragged | | `open` | `boolean` | Whether the color picker is open | | `inline` | `boolean` | Whether the color picker is rendered inline | | `value` | `Color` | The current color value (as a string) | | `valueAsString` | `string` | The current color value (as a Color object) | | `setValue` | `(value: string | Color) => void` | Function to set the color value | | `getChannelValue` | `(channel: ColorChannel) => string` | Function to set the color value | | `getChannelValueText` | `(channel: ColorChannel, locale: string) => string` | Function to get the formatted and localized value of a specific channel | | `setChannelValue` | `(channel: ColorChannel, value: number) => void` | Function to set the color value of a specific channel | | `format` | `ColorFormat` | The current color format | | `setFormat` | `(format: ColorFormat) => void` | Function to set the color format | | `alpha` | `number` | The alpha value of the color | | `setAlpha` | `(value: number) => void` | Function to set the color alpha | | `setOpen` | `(open: boolean) => void` | Function to open or close the color picker | ## Accessibility ### Keyboard Support **`Enter`** Description: When focus is on the trigger, opens the color picker
When focus is on a trigger of a swatch, selects the color (and closes the color picker)
When focus is on the input or channel inputs, selects the color
**`ArrowLeft`** Description: When focus is on the color area, decreases the hue value of the color
When focus is on the channel sliders, decreases the value of the channel
**`ArrowRight`** Description: When focus is on the color area, increases the hue value of the color
When focus is on the channel sliders, increases the value of the channel
**`ArrowUp`** Description: When focus is on the color area, increases the saturation value of the color
When focus is on the channel sliders, increases the value of the channel
**`ArrowDown`** Description: When focus is on the color area, decreases the saturation value of the color
When focus is on the channel sliders, decreases the value of the channel
**`Esc`** Description: Closes the color picker and moves focus to the trigger # Combobox ## Anatomy ```tsx ``` ## Examples ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const Basic = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Orange', value: 'orange' }, { label: 'Mango', value: 'mango' }, { label: 'Pineapple', value: 'pineapple' }, { label: 'Strawberry', value: 'strawberry' }, ], filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Favorite Fruit
{(item) => ( {item.label} )}
) } ``` ### Auto Highlight Automatically highlight the first matching item as the user types by setting `inputBehavior="autohighlight"`. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const AutoHighlight = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { label: 'Engineering', value: 'engineering' }, { label: 'Marketing', value: 'marketing' }, { label: 'Sales', value: 'sales' }, { label: 'Finance', value: 'finance' }, { label: 'Human Resources', value: 'hr' }, { label: 'Operations', value: 'operations' }, { label: 'Product', value: 'product' }, { label: 'Customer Success', value: 'customer-success' }, { label: 'Legal', value: 'legal' }, { label: 'Information Technology', value: 'information-technology' }, { label: 'Design', value: 'design' }, ], filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Department
Clear Open
No results found {(item) => ( {item.label} )}
) } ``` ### Inline Autocomplete Complete the input value with the first matching item by setting `inputBehavior="autocomplete"`. Use with `startsWith` filter for best results. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const InlineAutocomplete = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { label: 'Whale', value: 'whale' }, { label: 'Dolphin', value: 'dolphin' }, { label: 'Shark', value: 'shark' }, { label: 'Octopus', value: 'octopus' }, { label: 'Jellyfish', value: 'jellyfish' }, { label: 'Seahorse', value: 'seahorse' }, ], filter: filterFn().startsWith, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Sea Creature
Clear Open
No results found {(item) => ( {item.label} )}
) } ``` ### Grouping To group related combobox items, use the `groupBy` prop on the collection and `collection.group()` to iterate the groups. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' const initialItems = [ { label: 'Canada', value: 'ca', continent: 'North America' }, { label: 'United States', value: 'us', continent: 'North America' }, { label: 'Mexico', value: 'mx', continent: 'North America' }, { label: 'United Kingdom', value: 'uk', continent: 'Europe' }, { label: 'Germany', value: 'de', continent: 'Europe' }, { label: 'France', value: 'fr', continent: 'Europe' }, { label: 'Japan', value: 'jp', continent: 'Asia' }, { label: 'South Korea', value: 'kr', continent: 'Asia' }, { label: 'China', value: 'cn', continent: 'Asia' }, ] export const Grouping = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems, filter: filterFn().contains, groupBy: (item) => item.continent, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Country
{([continent, group]) => ( {continent} {(item) => ( {item.label} )} )}
) } ``` ### Field The `Field` component helps manage form-related state and accessibility attributes of a combobox. It includes handling ARIA labels, helper text, and error text to ensure proper accessibility. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { Field } from '@ark-ui/solid/field' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import styles from 'styles/combobox.module.css' import field from 'styles/field.module.css' const initialItems = [ { label: 'Engineering', value: 'engineering' }, { label: 'Design', value: 'design' }, { label: 'Marketing', value: 'marketing' }, { label: 'Sales', value: 'sales' }, { label: 'Human Resources', value: 'hr' }, { label: 'Finance', value: 'finance' }, ] export const WithField = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems, filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Department
Clear Open
{(item) => ( {item.label} )}
Select your primary department Department is required
) } ``` ### Context Access the combobox's state with `Combobox.Context` or the `useComboboxContext` hook—useful for displaying the selected value or building custom UI. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const Context = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { label: 'Small', value: 'sm' }, { label: 'Medium', value: 'md' }, { label: 'Large', value: 'lg' }, { label: 'Extra Large', value: 'xl' }, ], filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( {(context) =>

Selected: {context().valueAsString || 'None'}

}
Size
{(item) => ( {item.label} )}
) } ``` ### Root Provider An alternative way to control the combobox is to use the `RootProvider` component and the `useCombobox` hook. This way you can access the state and methods from outside the component. ```tsx import { Combobox, useCombobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/combobox.module.css' const initialItems = [ { label: 'Designer', value: 'designer' }, { label: 'Developer', value: 'developer' }, { label: 'Product Manager', value: 'pm' }, { label: 'Data Scientist', value: 'data-scientist' }, { label: 'DevOps Engineer', value: 'devops' }, { label: 'Marketing Lead', value: 'marketing' }, ] export const RootProvider = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems, filter: filterFn().contains, }) const combobox = useCombobox({ get collection() { return collection() }, onInputValueChange(details) { filter(details.inputValue) }, }) return (
Job Title
{(item) => ( {item.label} )}
) } ``` ### Links Use the `asChild` prop to render the combobox items as links. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' const initialItems = [ { label: 'GitHub', href: 'https://github.com', value: 'github' }, { label: 'Stack Overflow', href: 'https://stackoverflow.com', value: 'stackoverflow' }, { label: 'MDN Web Docs', href: 'https://developer.mozilla.org', value: 'mdn' }, { label: 'npm', href: 'https://www.npmjs.com', value: 'npm' }, { label: 'TypeScript', href: 'https://www.typescriptlang.org', value: 'typescript' }, { label: 'React', href: 'https://react.dev', value: 'react' }, ] export const Links = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems, filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Developer Resources
{(item) => ( }> {item.label} )}
) } ``` ### Rehydrate When a combobox has a `defaultValue` or `value` but the `collection` is not loaded yet, you can rehydrate the value to populate the input. ```tsx import { Combobox, useCombobox, useComboboxContext, useListCollection } from '@ark-ui/solid/combobox' import { For, createEffect, createRenderEffect, createSignal, on } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' import { useAsync } from './use-async.ts' function ComboboxRehydrateValue() { const combobox = useComboboxContext() let hydrated = false createRenderEffect(() => { if (combobox().value.length && combobox().collection.size && !hydrated) { combobox().syncSelectedItems() hydrated = true } }) return null } export const RehydrateValue = () => { const [inputValue, setInputValue] = createSignal('') const { collection, set } = useListCollection({ initialItems: [], itemToString: (item) => item.name, itemToValue: (item) => item.name, }) const combobox = useCombobox(() => ({ collection: collection(), defaultValue: ['C-3PO'], placeholder: 'Example: Dexter', inputValue: inputValue(), onInputValueChange: (e) => setInputValue(e.inputValue), })) const state = useAsync(async (signal) => { const response = await fetch(`https://swapi.py4e.com/api/people/?search=${inputValue()}`, { signal }) const data = await response.json() set(data.results) }) createEffect(on(inputValue, () => state.load())) return ( Search Star Wars Characters {state.loading() ? ( Loading... ) : state.error() ? ( {state.error()?.message} ) : ( {(item) => ( {item.name} - {item.height}cm / {item.mass}kg )} )} ) } interface Character { name: string height: string mass: string created: string edited: string url: string } ``` ### Highlight Text Highlight the matching search text in combobox items based on the user's input. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { Highlight } from '@ark-ui/solid/highlight' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const HighlightMatchingText = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { label: 'John Smith', value: 'john-smith' }, { label: 'Jane Doe', value: 'jane-doe' }, { label: 'Bob Johnson', value: 'bob-johnson' }, { label: 'Alice Williams', value: 'alice-williams' }, { label: 'Charlie Brown', value: 'charlie-brown' }, { label: 'Diana Ross', value: 'diana-ross' }, ], filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Assignee
Clear Open
{(item) => ( {(context) => } )}
) } ``` ### Dynamic Generate combobox items dynamically based on user input. This is useful for creating suggestions or autocomplete functionality. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' const suggestList = ['gmail.com', 'yahoo.com', 'ark-ui.com'] export const Dynamic = () => { const { collection, set } = useListCollection({ initialItems: [], }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { if (details.reason === 'input-change') { const items = suggestList.map((item) => `${details.inputValue}@${item}`) set(items) } } return ( Email
Open
{(item) => ( {item} )}
) } ``` ### Creatable Allow users to create new options when their search doesn't match any existing items. This is useful for tags, categories, or other custom values. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { createSignal, For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' interface Item { label: string value: string __new__?: boolean } const NEW_OPTION_VALUE = '[[new]]' const createNewOption = (value: string): Item => ({ label: value, value: NEW_OPTION_VALUE }) const isNewOptionValue = (value: string) => value === NEW_OPTION_VALUE const replaceNewOptionValue = (values: string[], value: string) => values.map((v) => (v === NEW_OPTION_VALUE ? value : v)) const getNewOptionData = (inputValue: string): Item => ({ label: inputValue, value: inputValue, __new__: true }) export const Creatable = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter, upsert, update, remove } = useListCollection({ initialItems: [ { label: 'Bug', value: 'bug' }, { label: 'Feature', value: 'feature' }, { label: 'Enhancement', value: 'enhancement' }, { label: 'Documentation', value: 'docs' }, ], filter: filterFn().contains, }) const isValidNewOption = (inputValue: string) => { const exactOptionMatch = collection().items.filter((item) => item.label.toLowerCase() === inputValue.toLowerCase()).length > 0 return !exactOptionMatch && inputValue.trim().length > 0 } const [selectedValue, setSelectedValue] = createSignal([]) const [inputValue, setInputValue] = createSignal('') const handleInputChange = ({ inputValue: newInputValue, reason }: Combobox.InputValueChangeDetails) => { if (reason === 'input-change' || reason === 'item-select') { if (isValidNewOption(newInputValue)) { upsert(NEW_OPTION_VALUE, createNewOption(newInputValue)) } else if (newInputValue.trim().length === 0) { remove(NEW_OPTION_VALUE) } filter(newInputValue) } setInputValue(newInputValue) } const handleOpenChange = ({ reason }: Combobox.OpenChangeDetails) => { if (reason === 'trigger-click') { filter('') } } const handleValueChange = ({ value }: Combobox.ValueChangeDetails) => { setSelectedValue(replaceNewOptionValue(value, inputValue())) if (value.includes(NEW_OPTION_VALUE)) { console.log('New Option Created', inputValue()) update(NEW_OPTION_VALUE, getNewOptionData(inputValue())) } } return ( Label
Clear Open
{(item) => ( {isNewOptionValue(item.value) ? ( + Create "{item.label}" ) : ( {item.label} {item.__new__ ? '(new)' : ''} )} )}
) } ``` ### Multiple Selection Enable multiple selection by setting the `multiple` prop. Selected items can be displayed as tags above the input. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { CheckIcon, ChevronsUpDownIcon } from 'lucide-solid' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const Multiple = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter, remove } = useListCollection({ initialItems: [ { label: 'JavaScript', value: 'js' }, { label: 'TypeScript', value: 'ts' }, { label: 'Python', value: 'python' }, { label: 'Go', value: 'go' }, { label: 'Rust', value: 'rust' }, { label: 'Java', value: 'java' }, ], filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } const handleValueChange = (details: Combobox.ValueChangeDetails) => { remove(...details.value) } return ( Skills {(context) => (
{context().selectedItems.length === 0 && None selected} {(item: any) => {item.label}}
)}
No skills found {(item) => ( {item.label} )}
) } ``` ### Async Search Load options asynchronously based on user input using the `useAsyncList` hook. This is useful for searching large datasets or fetching data from an API. ```tsx import { useAsyncList } from '@ark-ui/solid/collection' import { Combobox, createListCollection } from '@ark-ui/solid/combobox' import { CheckIcon, ChevronsUpDownIcon, LoaderIcon, XIcon } from 'lucide-solid' import { For, createMemo } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' interface Movie { id: string title: string year: number director: string genre: string } export const AsyncSearch = () => { const list = useAsyncList({ async load({ filterText, signal }) { if (!filterText) return { items: [] } await new Promise((resolve) => setTimeout(resolve, 300)) if (signal?.aborted) return { items: [] } const items = allMovies.filter( (movie) => movie.title.toLowerCase().includes(filterText.toLowerCase()) || movie.director.toLowerCase().includes(filterText.toLowerCase()) || movie.genre.toLowerCase().includes(filterText.toLowerCase()), ) return { items } }, }) const collection = createMemo(() => createListCollection({ items: list().items, itemToString: (item) => item.title, itemToValue: (item) => item.id, }), ) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { if (details.reason === 'input-change') { list().setFilterText(details.inputValue) } } return ( Movie
{list().loading ? (
Searching...
) : list().error ? (
{list().error.message}
) : list().items.length === 0 ? (
{list().filterText ? 'No results found' : 'Start typing to search movies...'}
) : ( {(movie) => ( {movie.title} {movie.year} · {movie.director} )} )}
) } const allMovies: Movie[] = [ { id: 'inception', title: 'Inception', year: 2010, director: 'Christopher Nolan', genre: 'Sci-Fi' }, { id: 'the-dark-knight', title: 'The Dark Knight', year: 2008, director: 'Christopher Nolan', genre: 'Action' }, { id: 'pulp-fiction', title: 'Pulp Fiction', year: 1994, director: 'Quentin Tarantino', genre: 'Crime' }, { id: 'the-godfather', title: 'The Godfather', year: 1972, director: 'Francis Ford Coppola', genre: 'Crime' }, { id: 'forrest-gump', title: 'Forrest Gump', year: 1994, director: 'Robert Zemeckis', genre: 'Drama' }, { id: 'the-matrix', title: 'The Matrix', year: 1999, director: 'The Wachowskis', genre: 'Sci-Fi' }, { id: 'interstellar', title: 'Interstellar', year: 2014, director: 'Christopher Nolan', genre: 'Sci-Fi' }, { id: 'parasite', title: 'Parasite', year: 2019, director: 'Bong Joon-ho', genre: 'Thriller' }, { id: 'the-shawshank-redemption', title: 'The Shawshank Redemption', year: 1994, director: 'Frank Darabont', genre: 'Drama', }, { id: 'fight-club', title: 'Fight Club', year: 1999, director: 'David Fincher', genre: 'Drama' }, { id: 'goodfellas', title: 'Goodfellas', year: 1990, director: 'Martin Scorsese', genre: 'Crime' }, { id: 'the-silence-of-the-lambs', title: 'The Silence of the Lambs', year: 1991, director: 'Jonathan Demme', genre: 'Thriller', }, ] ``` ### Virtualized For very large lists, use virtualization with `@tanstack/virtual` to render only the visible items. Pass the `scrollToIndexFn` prop to enable keyboard navigation within the virtualized list. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { createVirtualizer } from '@tanstack/solid-virtual' import { CheckIcon, ChevronsUpDownIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const Virtualized = () => { let contentRef: HTMLDivElement | undefined const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter, reset } = useListCollection({ initialItems: countries, filter: filterFn().startsWith, }) const virtualizer = createVirtualizer({ get count() { return collection().size }, getScrollElement: () => contentRef ?? null, estimateSize: () => 32, overscan: 10, }) const handleScrollToIndex: Combobox.RootProps['scrollToIndexFn'] = (details) => { virtualizer.scrollToIndex(details.index, { align: 'center', behavior: 'auto', }) } const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Country
{virtualizer.getVirtualItems().map((virtualItem) => { const item = collection().items[virtualItem.index] return ( {item.emoji} {item.label} ) })}
) } interface Country { value: string label: string emoji: string } const countries: Country[] = [ { value: 'AD', label: 'Andorra', emoji: '🇦🇩' }, { value: 'AE', label: 'United Arab Emirates', emoji: '🇦🇪' }, { value: 'AF', label: 'Afghanistan', emoji: '🇦🇫' }, { value: 'AG', label: 'Antigua and Barbuda', emoji: '🇦🇬' }, { value: 'AI', label: 'Anguilla', emoji: '🇦🇮' }, { value: 'AL', label: 'Albania', emoji: '🇦🇱' }, { value: 'AM', label: 'Armenia', emoji: '🇦🇲' }, { value: 'AO', label: 'Angola', emoji: '🇦🇴' }, { value: 'AQ', label: 'Antarctica', emoji: '🇦🇶' }, { value: 'AR', label: 'Argentina', emoji: '🇦🇷' }, { value: 'AS', label: 'American Samoa', emoji: '🇦🇸' }, { value: 'AT', label: 'Austria', emoji: '🇦🇹' }, { value: 'AU', label: 'Australia', emoji: '🇦🇺' }, { value: 'AW', label: 'Aruba', emoji: '🇦🇼' }, { value: 'AX', label: 'Åland Islands', emoji: '🇦🇽' }, { value: 'AZ', label: 'Azerbaijan', emoji: '🇦🇿' }, { value: 'BA', label: 'Bosnia and Herzegovina', emoji: '🇧🇦' }, { value: 'BB', label: 'Barbados', emoji: '🇧🇧' }, { value: 'BD', label: 'Bangladesh', emoji: '🇧🇩' }, { value: 'BE', label: 'Belgium', emoji: '🇧🇪' }, { value: 'BF', label: 'Burkina Faso', emoji: '🇧🇫' }, { value: 'BG', label: 'Bulgaria', emoji: '🇧🇬' }, { value: 'BH', label: 'Bahrain', emoji: '🇧🇭' }, { value: 'BI', label: 'Burundi', emoji: '🇧🇮' }, { value: 'BJ', label: 'Benin', emoji: '🇧🇯' }, { value: 'BL', label: 'Saint Barthélemy', emoji: '🇧🇱' }, { value: 'BM', label: 'Bermuda', emoji: '🇧🇲' }, { value: 'BN', label: 'Brunei', emoji: '🇧🇳' }, { value: 'BO', label: 'Bolivia', emoji: '🇧🇴' }, { value: 'BR', label: 'Brazil', emoji: '🇧🇷' }, { value: 'BS', label: 'Bahamas', emoji: '🇧🇸' }, { value: 'BT', label: 'Bhutan', emoji: '🇧🇹' }, { value: 'BW', label: 'Botswana', emoji: '🇧🇼' }, { value: 'BY', label: 'Belarus', emoji: '🇧🇾' }, { value: 'BZ', label: 'Belize', emoji: '🇧🇿' }, { value: 'CA', label: 'Canada', emoji: '🇨🇦' }, { value: 'CD', label: 'Congo', emoji: '🇨🇩' }, { value: 'CF', label: 'Central African Republic', emoji: '🇨🇫' }, { value: 'CH', label: 'Switzerland', emoji: '🇨🇭' }, { value: 'CI', label: "Côte d'Ivoire", emoji: '🇨🇮' }, { value: 'CK', label: 'Cook Islands', emoji: '🇨🇰' }, { value: 'CL', label: 'Chile', emoji: '🇨🇱' }, { value: 'CM', label: 'Cameroon', emoji: '🇨🇲' }, { value: 'CN', label: 'China', emoji: '🇨🇳' }, { value: 'CO', label: 'Colombia', emoji: '🇨🇴' }, { value: 'CR', label: 'Costa Rica', emoji: '🇨🇷' }, { value: 'CU', label: 'Cuba', emoji: '🇨🇺' }, { value: 'CV', label: 'Cabo Verde', emoji: '🇨🇻' }, { value: 'CY', label: 'Cyprus', emoji: '🇨🇾' }, { value: 'CZ', label: 'Czech Republic', emoji: '🇨🇿' }, { value: 'DE', label: 'Germany', emoji: '🇩🇪' }, { value: 'DJ', label: 'Djibouti', emoji: '🇩🇯' }, { value: 'DK', label: 'Denmark', emoji: '🇩🇰' }, { value: 'DM', label: 'Dominica', emoji: '🇩🇲' }, { value: 'DO', label: 'Dominican Republic', emoji: '🇩🇴' }, { value: 'DZ', label: 'Algeria', emoji: '🇩🇿' }, { value: 'EC', label: 'Ecuador', emoji: '🇪🇨' }, { value: 'EE', label: 'Estonia', emoji: '🇪🇪' }, { value: 'EG', label: 'Egypt', emoji: '🇪🇬' }, { value: 'ER', label: 'Eritrea', emoji: '🇪🇷' }, { value: 'ES', label: 'Spain', emoji: '🇪🇸' }, { value: 'ET', label: 'Ethiopia', emoji: '🇪🇹' }, { value: 'FI', label: 'Finland', emoji: '🇫🇮' }, { value: 'FJ', label: 'Fiji', emoji: '🇫🇯' }, { value: 'FK', label: 'Falkland Islands', emoji: '🇫🇰' }, { value: 'FM', label: 'Micronesia', emoji: '🇫🇲' }, { value: 'FO', label: 'Faroe Islands', emoji: '🇫🇴' }, { value: 'FR', label: 'France', emoji: '🇫🇷' }, { value: 'GA', label: 'Gabon', emoji: '🇬🇦' }, { value: 'GB', label: 'United Kingdom', emoji: '🇬🇧' }, { value: 'GD', label: 'Grenada', emoji: '🇬🇩' }, { value: 'GE', label: 'Georgia', emoji: '🇬🇪' }, { value: 'GH', label: 'Ghana', emoji: '🇬🇭' }, { value: 'GI', label: 'Gibraltar', emoji: '🇬🇮' }, { value: 'GL', label: 'Greenland', emoji: '🇬🇱' }, { value: 'GM', label: 'Gambia', emoji: '🇬🇲' }, { value: 'GN', label: 'Guinea', emoji: '🇬🇳' }, { value: 'GQ', label: 'Equatorial Guinea', emoji: '🇬🇶' }, { value: 'GR', label: 'Greece', emoji: '🇬🇷' }, { value: 'GT', label: 'Guatemala', emoji: '🇬🇹' }, { value: 'GU', label: 'Guam', emoji: '🇬🇺' }, { value: 'GW', label: 'Guinea-Bissau', emoji: '🇬🇼' }, { value: 'GY', label: 'Guyana', emoji: '🇬🇾' }, { value: 'HK', label: 'Hong Kong', emoji: '🇭🇰' }, { value: 'HN', label: 'Honduras', emoji: '🇭🇳' }, { value: 'HR', label: 'Croatia', emoji: '🇭🇷' }, { value: 'HT', label: 'Haiti', emoji: '🇭🇹' }, { value: 'HU', label: 'Hungary', emoji: '🇭🇺' }, { value: 'ID', label: 'Indonesia', emoji: '🇮🇩' }, { value: 'IE', label: 'Ireland', emoji: '🇮🇪' }, { value: 'IL', label: 'Israel', emoji: '🇮🇱' }, { value: 'IM', label: 'Isle of Man', emoji: '🇮🇲' }, { value: 'IN', label: 'India', emoji: '🇮🇳' }, { value: 'IQ', label: 'Iraq', emoji: '🇮🇶' }, { value: 'IR', label: 'Iran', emoji: '🇮🇷' }, { value: 'IS', label: 'Iceland', emoji: '🇮🇸' }, { value: 'IT', label: 'Italy', emoji: '🇮🇹' }, { value: 'JE', label: 'Jersey', emoji: '🇯🇪' }, { value: 'JM', label: 'Jamaica', emoji: '🇯🇲' }, { value: 'JO', label: 'Jordan', emoji: '🇯🇴' }, { value: 'JP', label: 'Japan', emoji: '🇯🇵' }, { value: 'KE', label: 'Kenya', emoji: '🇰🇪' }, { value: 'KG', label: 'Kyrgyzstan', emoji: '🇰🇬' }, { value: 'KH', label: 'Cambodia', emoji: '🇰🇭' }, { value: 'KI', label: 'Kiribati', emoji: '🇰🇮' }, { value: 'KM', label: 'Comoros', emoji: '🇰🇲' }, { value: 'KN', label: 'Saint Kitts and Nevis', emoji: '🇰🇳' }, { value: 'KP', label: 'North Korea', emoji: '🇰🇵' }, { value: 'KR', label: 'South Korea', emoji: '🇰🇷' }, { value: 'KW', label: 'Kuwait', emoji: '🇰🇼' }, { value: 'KY', label: 'Cayman Islands', emoji: '🇰🇾' }, { value: 'KZ', label: 'Kazakhstan', emoji: '🇰🇿' }, { value: 'LA', label: 'Laos', emoji: '🇱🇦' }, { value: 'LB', label: 'Lebanon', emoji: '🇱🇧' }, { value: 'LC', label: 'Saint Lucia', emoji: '🇱🇨' }, { value: 'LI', label: 'Liechtenstein', emoji: '🇱🇮' }, { value: 'LK', label: 'Sri Lanka', emoji: '🇱🇰' }, { value: 'LR', label: 'Liberia', emoji: '🇱🇷' }, { value: 'LS', label: 'Lesotho', emoji: '🇱🇸' }, { value: 'LT', label: 'Lithuania', emoji: '🇱🇹' }, { value: 'LU', label: 'Luxembourg', emoji: '🇱🇺' }, { value: 'LV', label: 'Latvia', emoji: '🇱🇻' }, { value: 'LY', label: 'Libya', emoji: '🇱🇾' }, { value: 'MA', label: 'Morocco', emoji: '🇲🇦' }, { value: 'MC', label: 'Monaco', emoji: '🇲🇨' }, { value: 'MD', label: 'Moldova', emoji: '🇲🇩' }, { value: 'ME', label: 'Montenegro', emoji: '🇲🇪' }, { value: 'MG', label: 'Madagascar', emoji: '🇲🇬' }, { value: 'MH', label: 'Marshall Islands', emoji: '🇲🇭' }, { value: 'MK', label: 'North Macedonia', emoji: '🇲🇰' }, { value: 'ML', label: 'Mali', emoji: '🇲🇱' }, { value: 'MM', label: 'Myanmar', emoji: '🇲🇲' }, { value: 'MN', label: 'Mongolia', emoji: '🇲🇳' }, { value: 'MO', label: 'Macao', emoji: '🇲🇴' }, { value: 'MR', label: 'Mauritania', emoji: '🇲🇷' }, { value: 'MS', label: 'Montserrat', emoji: '🇲🇸' }, { value: 'MT', label: 'Malta', emoji: '🇲🇹' }, { value: 'MU', label: 'Mauritius', emoji: '🇲🇺' }, { value: 'MV', label: 'Maldives', emoji: '🇲🇻' }, { value: 'MW', label: 'Malawi', emoji: '🇲🇼' }, { value: 'MX', label: 'Mexico', emoji: '🇲🇽' }, { value: 'MY', label: 'Malaysia', emoji: '🇲🇾' }, { value: 'MZ', label: 'Mozambique', emoji: '🇲🇿' }, { value: 'NA', label: 'Namibia', emoji: '🇳🇦' }, { value: 'NC', label: 'New Caledonia', emoji: '🇳🇨' }, { value: 'NE', label: 'Niger', emoji: '🇳🇪' }, { value: 'NF', label: 'Norfolk Island', emoji: '🇳🇫' }, { value: 'NG', label: 'Nigeria', emoji: '🇳🇬' }, { value: 'NI', label: 'Nicaragua', emoji: '🇳🇮' }, { value: 'NL', label: 'Netherlands', emoji: '🇳🇱' }, { value: 'NO', label: 'Norway', emoji: '🇳🇴' }, { value: 'NP', label: 'Nepal', emoji: '🇳🇵' }, { value: 'NR', label: 'Nauru', emoji: '🇳🇷' }, { value: 'NU', label: 'Niue', emoji: '🇳🇺' }, { value: 'NZ', label: 'New Zealand', emoji: '🇳🇿' }, { value: 'OM', label: 'Oman', emoji: '🇴🇲' }, { value: 'PA', label: 'Panama', emoji: '🇵🇦' }, { value: 'PE', label: 'Peru', emoji: '🇵🇪' }, { value: 'PF', label: 'French Polynesia', emoji: '🇵🇫' }, { value: 'PG', label: 'Papua New Guinea', emoji: '🇵🇬' }, { value: 'PH', label: 'Philippines', emoji: '🇵🇭' }, { value: 'PK', label: 'Pakistan', emoji: '🇵🇰' }, { value: 'PL', label: 'Poland', emoji: '🇵🇱' }, { value: 'PR', label: 'Puerto Rico', emoji: '🇵🇷' }, { value: 'PS', label: 'Palestine', emoji: '🇵🇸' }, { value: 'PT', label: 'Portugal', emoji: '🇵🇹' }, { value: 'PW', label: 'Palau', emoji: '🇵🇼' }, { value: 'PY', label: 'Paraguay', emoji: '🇵🇾' }, { value: 'QA', label: 'Qatar', emoji: '🇶🇦' }, { value: 'RO', label: 'Romania', emoji: '🇷🇴' }, { value: 'RS', label: 'Serbia', emoji: '🇷🇸' }, { value: 'RU', label: 'Russia', emoji: '🇷🇺' }, { value: 'RW', label: 'Rwanda', emoji: '🇷🇼' }, { value: 'SA', label: 'Saudi Arabia', emoji: '🇸🇦' }, { value: 'SB', label: 'Solomon Islands', emoji: '🇸🇧' }, { value: 'SC', label: 'Seychelles', emoji: '🇸🇨' }, { value: 'SD', label: 'Sudan', emoji: '🇸🇩' }, { value: 'SE', label: 'Sweden', emoji: '🇸🇪' }, { value: 'SG', label: 'Singapore', emoji: '🇸🇬' }, { value: 'SI', label: 'Slovenia', emoji: '🇸🇮' }, { value: 'SK', label: 'Slovakia', emoji: '🇸🇰' }, { value: 'SL', label: 'Sierra Leone', emoji: '🇸🇱' }, { value: 'SM', label: 'San Marino', emoji: '🇸🇲' }, { value: 'SN', label: 'Senegal', emoji: '🇸🇳' }, { value: 'SO', label: 'Somalia', emoji: '🇸🇴' }, { value: 'SR', label: 'Suriname', emoji: '🇸🇷' }, { value: 'SS', label: 'South Sudan', emoji: '🇸🇸' }, { value: 'ST', label: 'Sao Tome and Principe', emoji: '🇸🇹' }, { value: 'SV', label: 'El Salvador', emoji: '🇸🇻' }, { value: 'SY', label: 'Syria', emoji: '🇸🇾' }, { value: 'SZ', label: 'Eswatini', emoji: '🇸🇿' }, { value: 'TC', label: 'Turks and Caicos Islands', emoji: '🇹🇨' }, { value: 'TD', label: 'Chad', emoji: '🇹🇩' }, { value: 'TG', label: 'Togo', emoji: '🇹🇬' }, { value: 'TH', label: 'Thailand', emoji: '🇹🇭' }, { value: 'TJ', label: 'Tajikistan', emoji: '🇹🇯' }, { value: 'TK', label: 'Tokelau', emoji: '🇹🇰' }, { value: 'TL', label: 'Timor-Leste', emoji: '🇹🇱' }, { value: 'TM', label: 'Turkmenistan', emoji: '🇹🇲' }, { value: 'TN', label: 'Tunisia', emoji: '🇹🇳' }, { value: 'TO', label: 'Tonga', emoji: '🇹🇴' }, { value: 'TR', label: 'Türkiye', emoji: '🇹🇷' }, { value: 'TT', label: 'Trinidad and Tobago', emoji: '🇹🇹' }, { value: 'TV', label: 'Tuvalu', emoji: '🇹🇻' }, { value: 'TW', label: 'Taiwan', emoji: '🇹🇼' }, { value: 'TZ', label: 'Tanzania', emoji: '🇹🇿' }, { value: 'UA', label: 'Ukraine', emoji: '🇺🇦' }, { value: 'UG', label: 'Uganda', emoji: '🇺🇬' }, { value: 'US', label: 'United States', emoji: '🇺🇸' }, { value: 'UY', label: 'Uruguay', emoji: '🇺🇾' }, { value: 'UZ', label: 'Uzbekistan', emoji: '🇺🇿' }, { value: 'VA', label: 'Vatican City', emoji: '🇻🇦' }, { value: 'VC', label: 'Saint Vincent and the Grenadines', emoji: '🇻🇨' }, { value: 'VE', label: 'Venezuela', emoji: '🇻🇪' }, { value: 'VG', label: 'British Virgin Islands', emoji: '🇻🇬' }, { value: 'VI', label: 'U.S. Virgin Islands', emoji: '🇻🇮' }, { value: 'VN', label: 'Vietnam', emoji: '🇻🇳' }, { value: 'VU', label: 'Vanuatu', emoji: '🇻🇺' }, { value: 'WF', label: 'Wallis and Futuna', emoji: '🇼🇫' }, { value: 'WS', label: 'Samoa', emoji: '🇼🇸' }, { value: 'YE', label: 'Yemen', emoji: '🇾🇪' }, { value: 'YT', label: 'Mayotte', emoji: '🇾🇹' }, { value: 'ZA', label: 'South Africa', emoji: '🇿🇦' }, { value: 'ZM', label: 'Zambia', emoji: '🇿🇲' }, { value: 'ZW', label: 'Zimbabwe', emoji: '🇿🇼' }, ] ``` ### Custom Object Use the `itemToString` and `itemToValue` props to map custom objects to the required interface. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' export const CustomObject = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: [ { country: 'United States', code: 'US', flag: '🇺🇸' }, { country: 'Canada', code: 'CA', flag: '🇨🇦' }, { country: 'Australia', code: 'AU', flag: '🇦🇺' }, ], itemToString: (item) => item.country, itemToValue: (item) => item.code, filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( Country
Clear Open
{(item) => ( {item.flag} {item.country} )}
) } ``` ### Limit Results Use the `limit` property on `useListCollection` to limit the number of rendered items in the DOM. ```tsx import { Combobox, useListCollection } from '@ark-ui/solid/combobox' import { useFilter } from '@ark-ui/solid/locale' import { For } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/combobox.module.css' const cities = [ { label: 'New York', value: 'new-york' }, { label: 'Los Angeles', value: 'los-angeles' }, { label: 'Chicago', value: 'chicago' }, { label: 'Houston', value: 'houston' }, { label: 'Phoenix', value: 'phoenix' }, { label: 'Philadelphia', value: 'philadelphia' }, { label: 'San Antonio', value: 'san-antonio' }, { label: 'San Diego', value: 'san-diego' }, { label: 'Dallas', value: 'dallas' }, { label: 'San Jose', value: 'san-jose' }, { label: 'Austin', value: 'austin' }, { label: 'Jacksonville', value: 'jacksonville' }, { label: 'Fort Worth', value: 'fort-worth' }, { label: 'Columbus', value: 'columbus' }, { label: 'Charlotte', value: 'charlotte' }, { label: 'San Francisco', value: 'san-francisco' }, { label: 'Indianapolis', value: 'indianapolis' }, { label: 'Seattle', value: 'seattle' }, { label: 'Denver', value: 'denver' }, { label: 'Boston', value: 'boston' }, ] export const LimitResults = () => { const filterFn = useFilter({ sensitivity: 'base' }) const { collection, filter } = useListCollection({ initialItems: cities, limit: 5, filter: filterFn().contains, }) const handleInputChange = (details: Combobox.InputValueChangeDetails) => { filter(details.inputValue) } return ( City
Open
{(item) => ( {item.label} )}
) } ``` ## Guides ### Router Links Customize the `navigate` prop on `Combobox.Root` to integrate with your router. Using Tanstack Router: ```tsx import { Combobox } from '@ark-ui//combobox' import { useNavigate } from '@tanstack/react-router' function Demo() { const navigate = useNavigate() return ( { navigate({ to: e.node.href }) }} > {/* ... */} ) } ``` ### Custom Objects By default, the combobox collection expects an array of objects with `label` and `value` properties. In some cases, you may need to deal with custom objects. Use the `itemToString` and `itemToValue` props to map the custom object to the required interface. ```tsx const items = [ { country: 'United States', code: 'US', flag: '🇺🇸' }, { country: 'Canada', code: 'CA', flag: '🇨🇦' }, { country: 'Australia', code: 'AU', flag: '🇦🇺' }, // ... ] const { collection } = useListCollection({ initialItems: items, itemToString: (item) => item.country, itemToValue: (item) => item.code, }) ``` ### Type Safety The `Combobox.RootComponent` type enables you to create typed wrapper components that maintain full type safety for collection items. ```tsx const Combobox: ArkCombobox.RootComponent = (props) => { return {/* ... */} } ``` Use the wrapper with full type inference on `onValueChange` and other callbacks: ```tsx const App = () => { const { collection } = useListCollection({ initialItems: [ { label: 'React', value: 'react' }, { label: 'Vue', value: 'vue' }, ], }) return ( { // e.items is typed as Array<{ label: string, value: string }> console.log(e.items) }} > {/* ... */} ) } ``` ### Large Datasets The recommended way of managing large lists is to use the `limit` property on the `useListCollection` hook. This will limit the number of rendered items in the DOM to improve performance. ```tsx {3} const { collection } = useListCollection({ initialItems: items, limit: 10, }) ``` ### Available Size The following css variables are exposed to the `Combobox.Positioner` which you can use to style the `Combobox.Content` ```css /* width of the combobox control */ --reference-width: ; /* width of the available viewport */ --available-width: ; /* height of the available viewport */ --available-height: ; ``` For example, if you want to make sure the maximum height doesn't exceed the available height, you can use the following: ```css [data-scope='combobox'][data-part='content'] { max-height: calc(var(--available-height) - 100px); } ``` ## API Reference ### Props ### Root #### Props **`collection`** Type: `ListCollection` Required: true Default Value: `undefined` Description: The collection of items **`allowCustomValue`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to allow typing custom values in the input **`alwaysSubmitOnEnter`** Type: `boolean` Required: false Default Value: `false` Description: Whether to always submit on Enter key press, even if popup is open. Useful for single-field autocomplete forms where Enter should submit the form. **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`autoFocus`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to autofocus the input on mount **`closeOnSelect`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to close the combobox when an item is selected. **`composite`** Type: `boolean` Required: false Default Value: `true` Description: Whether the combobox is a composed with other composite widgets like tabs **`defaultHighlightedValue`** Type: `string` Required: false Default Value: `undefined` Description: The initial highlighted value of the combobox when rendered. Use when you don't need to control the highlighted value of the combobox. **`defaultInputValue`** Type: `string` Required: false Default Value: `""` Description: The initial value of the combobox's input when rendered. Use when you don't need to control the value of the combobox's input. **`defaultOpen`** Type: `boolean` Required: false Default Value: `undefined` Description: The initial open state of the combobox when rendered. Use when you don't need to control the open state of the combobox. **`defaultValue`** Type: `string[]` Required: false Default Value: `[]` Description: The initial value of the combobox's selected items when rendered. Use when you don't need to control the value of the combobox's selected items. **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the combobox is disabled **`disableLayer`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to disable registering this a dismissable layer **`form`** Type: `string` Required: false Default Value: `undefined` Description: The associate form of the combobox. **`highlightedValue`** Type: `string` Required: false Default Value: `undefined` Description: The controlled highlighted value of the combobox **`ids`** Type: `Partial<{ root: string label: string control: string input: string content: string trigger: string clearTrigger: string item: (id: string, index?: number | undefined) => string positioner: string itemGroup: (id: string | number) => string itemGroupLabel: (id: string | number) => string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the combobox. Useful for composition. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`inputBehavior`** Type: `'none' | 'autohighlight' | 'autocomplete'` Required: false Default Value: `"none"` Description: Defines the auto-completion behavior of the combobox. - `autohighlight`: The first focused item is highlighted as the user types - `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated **`inputValue`** Type: `string` Required: false Default Value: `undefined` Description: The controlled value of the combobox's input **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the combobox is invalid **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`loopFocus`** Type: `boolean` Required: false Default Value: `true` Description: Whether to loop the keyboard navigation through the items **`multiple`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to allow multiple selection. **Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`. It is recommended to render the selected items in a separate container. **`name`** Type: `string` Required: false Default Value: `undefined` Description: The `name` attribute of the combobox's input. Useful for form submission **`navigate`** Type: `(details: NavigateDetails) => void` Required: false Default Value: `undefined` Description: Function to navigate to the selected item **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`onFocusOutside`** Type: `(event: FocusOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when the focus is moved outside the component **`onHighlightChange`** Type: `(details: HighlightChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when an item is highlighted using the pointer or keyboard navigation. **`onInputValueChange`** Type: `(details: InputValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the input's value changes **`onInteractOutside`** Type: `(event: InteractOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when an interaction happens outside the component **`onOpenChange`** Type: `(details: OpenChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the popup is opened **`onPointerDownOutside`** Type: `(event: PointerDownOutsideEvent) => void` Required: false Default Value: `undefined` Description: Function called when the pointer is pressed down outside the component **`onSelect`** Type: `(details: SelectionDetails) => void` Required: false Default Value: `undefined` Description: Function called when an item is selected **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when a new item is selected **`open`** Type: `boolean` Required: false Default Value: `undefined` Description: The controlled open state of the combobox **`openOnChange`** Type: `boolean | ((details: InputValueChangeDetails) => boolean)` Required: false Default Value: `true` Description: Whether to show the combobox when the input value changes **`openOnClick`** Type: `boolean` Required: false Default Value: `false` Description: Whether to open the combobox popup on initial click on the input **`openOnKeyPress`** Type: `boolean` Required: false Default Value: `true` Description: Whether to open the combobox on arrow key press **`placeholder`** Type: `string` Required: false Default Value: `undefined` Description: The placeholder text of the combobox's input **`positioning`** Type: `PositioningOptions` Required: false Default Value: `{ placement: "bottom-start" }` Description: The positioning options to dynamically position the menu **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the combobox is readonly. This puts the combobox in a "non-editable" mode but the user can still interact with it **`required`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the combobox is required **`scrollToIndexFn`** Type: `(details: ScrollToIndexDetails) => void` Required: false Default Value: `undefined` Description: Function to scroll to a specific index **`selectionBehavior`** Type: `'clear' | 'replace' | 'preserve'` Required: false Default Value: `"replace"` Description: The behavior of the combobox input when an item is selected - `replace`: The selected item string is set as the input value - `clear`: The input value is cleared - `preserve`: The input value is preserved **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`translations`** Type: `IntlTranslations` Required: false Default Value: `undefined` Description: Specifies the localized strings that identifies the accessibility elements and their states **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. **`value`** Type: `string[]` Required: false Default Value: `undefined` Description: The controlled value of the combobox's selected items #### Data Attributes **`data-scope`**: combobox **`data-part`**: root **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only ### ClearTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: clear-trigger **`data-invalid`**: Present when invalid ### Content #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: content **`data-state`**: "open" | "closed" **`data-nested`**: listbox **`data-has-nested`**: listbox **`data-placement`**: The placement of the content **`data-side`**: The side of the trigger that the content is positioned on **`data-empty`**: Present when the content is empty ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: control **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid ### Empty #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Input #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: input **`data-invalid`**: Present when invalid **`data-autofocus`**: **`data-state`**: "open" | "closed" ### ItemGroupLabel #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### ItemGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: item-group **`data-empty`**: Present when the content is empty ### ItemIndicator #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: item-indicator **`data-state`**: "checked" | "unchecked" ### Item #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`item`** Type: `any` Required: false Default Value: `undefined` Description: The item to render **`persistFocus`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether hovering outside should clear the highlighted state #### Data Attributes **`data-scope`**: combobox **`data-part`**: item **`data-highlighted`**: Present when highlighted **`data-state`**: "checked" | "unchecked" **`data-disabled`**: Present when disabled **`data-value`**: The value of the item ### ItemText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: item-text **`data-state`**: "checked" | "unchecked" **`data-disabled`**: Present when disabled **`data-highlighted`**: Present when highlighted ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: label **`data-readonly`**: Present when read-only **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-required`**: Present when required **`data-focus`**: Present when focused ### List #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: combobox **`data-part`**: list **`data-empty`**: Present when the content is empty ### Positioner #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### RootProvider #### Props **`value`** Type: `UseComboboxReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. ### Trigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`focusable`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the trigger is focusable #### Data Attributes **`data-scope`**: combobox **`data-part`**: trigger **`data-state`**: "open" | "closed" **`data-invalid`**: Present when invalid **`data-focusable`**: **`data-readonly`**: Present when read-only **`data-disabled`**: Present when disabled ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `focused` | `boolean` | Whether the combobox is focused | | `open` | `boolean` | Whether the combobox is open | | `inputValue` | `string` | The value of the combobox input | | `highlightedValue` | `string | null` | The value of the highlighted item | | `highlightedItem` | `V | null` | The highlighted item | | `setHighlightValue` | `(value: string) => void` | The value of the combobox input | | `clearHighlightValue` | `VoidFunction` | Function to clear the highlighted value | | `syncSelectedItems` | `VoidFunction` | Function to sync the selected items with the value. Useful when `value` is updated from async sources. | | `selectedItems` | `V[]` | The selected items | | `hasSelectedItems` | `boolean` | Whether there's a selected item | | `value` | `string[]` | The selected item keys | | `valueAsString` | `string` | The string representation of the selected items | | `selectValue` | `(value: string) => void` | Function to select a value | | `setValue` | `(value: string[]) => void` | Function to set the value of the combobox | | `clearValue` | `(value?: string) => void` | Function to clear the value of the combobox | | `focus` | `VoidFunction` | Function to focus on the combobox input | | `setInputValue` | `(value: string, reason?: InputValueChangeReason) => void` | Function to set the input value of the combobox | | `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a combobox item | | `setOpen` | `(open: boolean, reason?: OpenChangeReason) => void` | Function to open or close the combobox | | `collection` | `ListCollection` | Function to toggle the combobox | | `reposition` | `(options?: Partial) => void` | Function to set the positioning options | | `multiple` | `boolean` | Whether the combobox allows multiple selections | | `disabled` | `boolean` | Whether the combobox is disabled | ## Accessibility Complies with the [Combobox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). ### Keyboard Support **`ArrowDown`** Description: When the combobox is closed, opens the listbox and highlights to the first option. When the combobox is open, moves focus to the next option. **`ArrowUp`** Description: When the combobox is closed, opens the listbox and highlights to the last option. When the combobox is open, moves focus to the previous option. **`Home`** Description: When the combobox is open, moves focus to the first option. **`End`** Description: When the combobox is open, moves focus to the last option. **`Escape`** Description: Closes the listbox. **`Enter`** Description: Selects the highlighted option and closes the combobox. **`Esc`** Description: Closes the combobox # Date Input ## Anatomy ```tsx ``` ## Examples ```tsx import { DateInput } from '@ark-ui/solid/date-input' import styles from 'styles/date-input.module.css' export const Basic = () => ( Date {(segment) => } ) ``` ### Default Value Use the `defaultValue` prop with `parseDate` to set the initial date value. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate } from '@internationalized/date' import styles from 'styles/date-input.module.css' export const DefaultValue = () => ( Date {(segment) => } ) ``` ### Controlled Use the `value` and `onValueChange` props to control the date input's value programmatically. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate, type DateValue } from '@internationalized/date' import { createSignal } from 'solid-js' import styles from 'styles/date-input.module.css' export const Controlled = () => { const [value, setValue] = createSignal([parseDate('2024-06-15')]) return ( setValue(e.value)}> Date {(segment) => } ) } ``` ### Root Provider An alternative way to control the date input is to use the `RootProvider` component and the `useDateInput` hook. This way you can access the state and methods from outside the component. ```tsx import { DateInput, useDateInput } from '@ark-ui/solid/date-input' import styles from 'styles/date-input.module.css' export const RootProvider = () => { const dateInput = useDateInput() return ( {dateInput().valueAsString.length > 0 ? dateInput().valueAsString : 'N/A'} Date {(segment) => } ) } ``` ### Granularity Use the `granularity` prop to control which date fields are displayed. Supported values are `day`, `hour`, `minute`, and `second`. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import styles from 'styles/date-input.module.css' export const Granularity = () => ( Date & Time {(segment) => } ) ``` ### Time Only To create a time-only input, set `granularity` to `minute` (or `second`) and provide a `formatter` that only includes time fields. Use the `hourCycle` prop to switch between 12 and 24 hour formats. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { DateFormatter, getLocalTimeZone } from '@internationalized/date' import styles from 'styles/date-input.module.css' const timeFormatter = new DateFormatter('en-US', { hour: '2-digit', minute: '2-digit', hourCycle: 'h23', }) export const TimeOnly = () => ( Time {(segment) => } ) ``` ### Range To create a date input that allows a range selection, set the `selectionMode` prop to `range` and render two `SegmentGroup` components with `index` props set to `0` and `1`. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import styles from 'styles/date-input.module.css' export const Range = () => ( Date Range {(segment) => } {(segment) => } ) ``` ### Min and Max Use the `min` and `max` props with `parseDate` to restrict the selectable date range. Dates outside this range will be marked as invalid. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate } from '@internationalized/date' import styles from 'styles/date-input.module.css' export const MinMax = () => ( Date (2024 only) {(segment) => } ) ``` ### Disabled Use the `disabled` prop to prevent user interaction with the date input. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate } from '@internationalized/date' import styles from 'styles/date-input.module.css' export const Disabled = () => ( Date {(segment) => } ) ``` ### Read Only Use the `readOnly` prop to make the date input non-editable while still being focusable. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate } from '@internationalized/date' import styles from 'styles/date-input.module.css' export const ReadOnly = () => ( Date {(segment) => } ) ``` ### Invalid Use the `invalid` prop to indicate an error state on the date input. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import styles from 'styles/date-input.module.css' export const Invalid = () => ( Date {(segment) => } ) ``` ### Leading Zeros Use the `shouldForceLeadingZeros` prop to toggle whether numeric segments are padded with a leading zero. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { parseDate } from '@internationalized/date' import { createSignal } from 'solid-js' import styles from 'styles/date-input.module.css' export const LeadingZeros = () => { const [shouldForceLeadingZeros, setShouldForceLeadingZeros] = createSignal(true) return (
Date {(segment) => }
) } ``` ### Localized Use the `locale` prop to set the language and regional formatting of the date segments. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { LocaleProvider } from '@ark-ui/solid/locale' import styles from 'styles/date-input.module.css' export const Localized = () => ( Date et heure {(segment) => } ) ``` ### RTL Set the `dir` prop to `rtl` for right-to-left language support. ```tsx import { DateInput } from '@ark-ui/solid/date-input' import { LocaleProvider } from '@ark-ui/solid/locale' import styles from 'styles/date-input.module.css' export const RTL = () => ( التاريخ {(segment) => } ) ``` ### With Clear Button Use `useDateInput` via `RootProvider` to access the `clearValue` method and render a clear button alongside the input. ```tsx import { DateInput, useDateInput } from '@ark-ui/solid/date-input' import { XIcon } from 'lucide-solid' import button from 'styles/button.module.css' import styles from 'styles/date-input.module.css' export const WithClearButton = () => { const dateInput = useDateInput() return ( Date {(segment) => } ) } ``` ### With Date Picker Combine `DateInput` with `DatePicker` by syncing their values using `onValueChange` to provide both typed and calendar-based date selection. ```tsx import { DateInput, useDateInput } from '@ark-ui/solid/date-input' import { DatePicker, useDatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/date-input.module.css' import datePickerStyles from 'styles/date-picker.module.css' export const WithDatePicker = () => { const datePicker = useDatePicker() const dateInput = useDateInput(() => ({ value: datePicker().value, onValueChange(details) { datePicker().setValue(details.value) }, })) return ( Date {(segment) => } {(datePicker) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(datePicker) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(datePicker) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ## API Reference ### Props ### Root #### Props **`allSegments`** Type: `Partial<{ year: boolean month: boolean day: boolean hour: boolean minute: boolean second: boolean dayPeriod: boolean era: boolean literal: boolean timeZoneName: boolean weekday: boolean unknown: boolean fractionalSecond: boolean }>` Required: false Default Value: `undefined` Description: The computed segments map for the formatter. **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`createCalendar`** Type: `(identifier: CalendarIdentifier) => Calendar` Required: false Default Value: `undefined` Description: A function that creates a calendar object for a given calendar identifier. Use this to support non-Gregorian calendars (e.g., Persian, Islamic, Buddhist). **`defaultPlaceholderValue`** Type: `DateValue` Required: false Default Value: `undefined` Description: The initial placeholder date when rendered. **`defaultValue`** Type: `DateValue[]` Required: false Default Value: `undefined` Description: The initial selected date(s) when rendered. Use when you don't need to control the selected date(s). **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date input is disabled. **`form`** Type: `string` Required: false Default Value: `undefined` Description: The `form` attribute of the hidden input element. **`format`** Type: `(date: DateValue, details: FormatDateDetails) => string` Required: false Default Value: `undefined` Description: The format function for converting a DateValue to a string. **`formatter`** Type: `DateFormatter` Required: false Default Value: `undefined` Description: The date formatter to use. **`granularity`** Type: `DateGranularity` Required: false Default Value: `"day"` Description: Determines the smallest unit that is displayed in the date input. **`hideTimeZone`** Type: `boolean` Required: false Default Value: `false` Description: Whether to hide the time zone segment when the value is a `ZonedDateTime`. Has no effect for values without a time zone. **`hourCycle`** Type: `HourCycle` Required: false Default Value: `undefined` Description: Whether to use 12-hour or 24-hour time format. By default, this is determined by the locale. **`ids`** Type: `Partial<{ root: string label: (index: number) => string control: string segmentGroup: (index: number) => string hiddenInput: (index: number) => string }>` Required: false Default Value: `undefined` Description: The ids of the elements in the date input. Useful for composition. **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date input is invalid **`isDateUnavailable`** Type: `(date: DateValue, locale: string) => boolean` Required: false Default Value: `undefined` Description: Returns whether a date is unavailable. When a committed date matches, the input is marked as invalid. **`locale`** Type: `string` Required: false Default Value: `"en-US"` Description: The locale (BCP 47 language tag) to use when formatting the date. **`max`** Type: `DateValue` Required: false Default Value: `undefined` Description: The maximum date that can be selected. **`min`** Type: `DateValue` Required: false Default Value: `undefined` Description: The minimum date that can be selected. **`name`** Type: `string` Required: false Default Value: `undefined` Description: The `name` attribute of the input element. **`onFocusChange`** Type: `(details: FocusChangeDetails) => void` Required: false Default Value: `undefined` Description: A function called when the date input gains or loses focus. **`onPlaceholderChange`** Type: `(details: PlaceholderChangeDetails) => void` Required: false Default Value: `undefined` Description: A function called when the placeholder value changes. **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the value changes. **`placeholderValue`** Type: `DateValue` Required: false Default Value: `undefined` Description: The controlled placeholder date. **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date input is read-only. **`required`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date input is required **`selectionMode`** Type: `SelectionMode` Required: false Default Value: `"single"` Description: The selection mode of the date input. - `single` - only one date can be entered - `range` - a range of dates can be entered (start and end) **`shouldForceLeadingZeros`** Type: `boolean` Required: false Default Value: `false` Description: Whether to always show leading zeros in month, day, and hour fields. When false, formatting follows the locale default (e.g. "1" instead of "01"). **`timeZone`** Type: `string` Required: false Default Value: `"UTC"` Description: The time zone to use **`translations`** Type: `IntlTranslations` Required: false Default Value: `undefined` Description: The localized messages to use. **`value`** Type: `DateValue[]` Required: false Default Value: `undefined` Description: The controlled selected date(s). #### Data Attributes **`data-scope`**: date-input **`data-part`**: root **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-input **`data-part`**: control **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-focus`**: Present when focused ### HiddenInput #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`index`** Type: `number` Required: false Default Value: `undefined` Description: undefined ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-input **`data-part`**: label **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid ### RootProvider #### Props **`value`** Type: `UseDateInputReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### SegmentGroup #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`index`** Type: `number` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: date-input **`data-part`**: segment-group **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-focus`**: Present when focused ### Segment #### Props **`segment`** Type: `DateSegment` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-input **`data-part`**: segment **`data-type`**: The type of the item **`data-readonly`**: Present when read-only **`data-disabled`**: Present when disabled **`data-value`**: The value of the item **`data-editable`**: **`data-placeholder-shown`**: Present when placeholder is shown ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `focused` | `boolean` | Whether the date input is focused | | `disabled` | `boolean` | Whether the date input is disabled | | `invalid` | `boolean` | Whether the date input is invalid | | `value` | `DateValue[]` | The selected date(s). | | `valueAsDate` | `Date[]` | The selected date(s) as Date objects. | | `valueAsString` | `string[]` | The selected date(s) as strings. | | `placeholderValue` | `DateValue` | The placeholder date. | | `displayValues` | `IncompleteDate[]` | Per-group editing state. Each IncompleteDate tracks which segments have been filled in by the user (non-null = entered, null = placeholder). | | `focus` | `VoidFunction` | Focuses the first segment. | | `setValue` | `(values: DateValue[]) => void` | Sets the selected date(s) to the given values. | | `clearValue` | `VoidFunction` | Clears the selected date(s). | | `getSegments` | `(props?: SegmentsProps | undefined) => DateSegment[]` | Returns the segments for the given index. | | `getSegmentState` | `(props: SegmentProps) => SegmentState` | Returns the state details for a given segment. | ## Accessibility Complies with the [Spinbutton WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/). # Date Picker ## Anatomy ```tsx ``` ## Examples ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const Basic = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Default Value Use the `defaultValue` prop with `parseDate` to set the initial date value. ```tsx import { DatePicker, parseDate } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const DefaultValue = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Controlled Use the `value` and `onValueChange` props to control the date picker's value programmatically. ```tsx import { DatePicker, parseDate } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index, createSignal } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const Controlled = () => { const [value, setValue] = createSignal([parseDate('2022-01-01')]) return ( setValue(e.value)}> Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Root Provider An alternative way to control the date picker is to use the `RootProvider` component and the `useDatePicker` hook. This way you can access the state and methods from outside the component. ```tsx import { DatePicker, useDatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const RootProvider = () => { const datePicker = useDatePicker() return ( <> Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Default View Use the `defaultView` prop to set which view (day, month, or year) the calendar opens to initially. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const DefaultView = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Month and Year Select Use `MonthSelect` and `YearSelect` components to create a header with dropdown selects for quick month/year navigation, alongside the prev/next triggers. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import styles from 'styles/date-picker.module.css' export const MonthYearSelect = () => { return ( Label {(context) => ( <>
{(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )}
) } ``` ### Range To create a date picker that allows a range selection, you need to: - Set the `selectionMode` prop to `range`. - Render multiple inputs with the `index` prop set to `0` and `1`. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon } from 'lucide-solid' import { Index, createMemo } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const RangeSelection = () => { return ( Label Clear Last 7 days
{(context) => ( {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => { const offset = createMemo(() => context().getOffset({ months: 1 })) return ( {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} ) }}
) } ``` ### Multiple Use the `selectionMode="multiple"` prop to allow selecting multiple dates. This example also shows how to display selected dates as removable tags. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { For, Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const MultiSelection = () => { return ( Label {(context) => (
{context().value.length === 0 ? ( Select dates... ) : ( {(date, index) => ( {date.toDate('UTC').toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', })} )} )}
)}
Clear
{(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )}
) } ``` ### Max Selected Dates Use the `maxSelectedDates` prop with `selectionMode="multiple"` to limit the number of dates that can be selected. In this example, users can select up to 3 dates. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/date-picker.module.css' export const MaxSelectedDates = () => { return ( {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} ) } ``` ### Multiple Months To create a date picker that displays multiple months side by side: - Set the `numOfMonths` prop to the number of months you want to display. - Use the `datePicker.getOffset({ months: 1 })` to get data for the next month. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index, createMemo } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const MultipleMonths = () => { return ( Label Clear
{(datePicker) => ( {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(datePicker) => { const offset = createMemo(() => datePicker().getOffset({ months: 1 })) return ( {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} ) }}
) } ``` ### Presets Use the `DatePicker.PresetTrigger` component to add quick-select preset options like "Last 7 days" or "This month". ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const Presets = () => { return ( Label Clear
Last 7 days Last 30 days This month
{(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )}
) } ``` ### Min and Max Use the `min` and `max` props with `parseDate` to restrict the selectable date range. Dates outside this range will be disabled. ```tsx import { DatePicker, parseDate } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const MinMax = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Unavailable Use the `isDateUnavailable` prop to mark specific dates as unavailable. This example disables weekends. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import type { DateValue } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const isWeekend = (date: DateValue) => { const dayOfWeek = date.toDate('UTC').getDay() return dayOfWeek === 0 || dayOfWeek === 6 } export const Unavailable = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Locale Use the `locale` prop to set the language and formatting, and `startOfWeek` to set the first day of the week (0 = Sunday, 1 = Monday, etc.). ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const Locale = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Month Picker Create a month-only picker by setting `defaultView="month"` and `minView="month"`. Use custom `format` and `parse` functions to handle month/year input format. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarDate, type DateValue } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const format = (date: DateValue) => { const month = date.month.toString().padStart(2, '0') const year = date.year.toString() return `${month}/${year}` } const parse = (string: string) => { const fullRegex = /^(\d{1,2})\/(\d{4})$/ const fullMatch = string.match(fullRegex) if (fullMatch) { const [_, month, year] = fullMatch.map(Number) return new CalendarDate(year, month, 1) } } export const MonthPicker = () => { return ( Label Clear {(context) => ( {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Year Picker Create a year-only picker by setting `defaultView="year"` and `minView="year"`. Use custom `format` and `parse` functions to handle year-only input format. ```tsx import { DatePicker, parseDate } from '@ark-ui/solid/date-picker' import type { DateValue } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const format = (date: DateValue) => date.year.toString() const parse = (string: string | undefined) => { if (string === '' || !string) return const year = Number(string) if (year < 100) { const currentYear = new Date().getFullYear() const currentCentury = Math.floor(currentYear / 100) * 100 return parseDate(new Date(currentCentury + year, 0)) } return parseDate(new Date(Number(string), 0)) } export const YearPicker = () => { return ( Label Clear {(context) => ( {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Inline Use the `inline` prop to display the date picker directly on the page, without a popup. > When using the `inline` prop, omit the `Portal`, `Positioner`, and `Content` components to render the calendar inline > within your layout. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import styles from 'styles/date-picker.module.css' export const Inline = () => { return ( {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Custom Parsing Use the `parse` prop to implement custom date parsing logic. This allows users to enter dates in flexible formats like "25/12" or "25/12/24" which are automatically converted to valid dates. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { CalendarDate, type DateValue } from '@internationalized/date' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const parse = (value: string) => { const fullRegex = /^(\d{1,2})\/(\d{1,2})\/(\d{2})$/ const fullMatch = value.match(fullRegex) if (fullMatch) { const [_, day, month, year] = fullMatch.map(Number) try { return new CalendarDate(year + 2000, month, day) } catch { return undefined } } const partialRegex = /^(\d{1,2})\/(\d{1,2})$/ const partialMatch = value.match(partialRegex) if (partialMatch) { const [_, day, month] = partialMatch.map(Number) const currentYear = new Date().getFullYear() try { return new CalendarDate(currentYear, month, day) } catch { return undefined } } const dayRegex = /^(\d{1,2})$/ const dayMatch = value.match(dayRegex) if (dayMatch) { const [_, day] = dayMatch.map(Number) const currentYear = new Date().getFullYear() return new CalendarDate(currentYear, 1, day) } return undefined } const format = (date: DateValue) => { const day = date.day.toString().padStart(2, '0') const month = date.month.toString().padStart(2, '0') const year = (date.year % 100).toString().padStart(2, '0') return `${day}/${month}/${year}` } export const FormatParse = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Month Picker Range Create a month range picker by combining `selectionMode="range"` with `defaultView="month"` and `minView="month"`. This is useful for selecting billing periods or date ranges by month. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarDate, type DateValue } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const format = (date: DateValue) => { const month = date.month.toString().padStart(2, '0') const year = date.year.toString() return `${month}/${year}` } const parse = (string: string) => { const fullRegex = /^(\d{1,2})\/(\d{4})$/ const fullMatch = string.match(fullRegex) if (fullMatch) { const [_, month, year] = fullMatch.map(Number) return new CalendarDate(year, month, 1) } } export const MonthPickerRange = () => { return ( Label Clear {(context) => ( {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Year Range Create a year range picker by combining `selectionMode="range"` with `defaultView="year"` and `minView="year"`. This is useful for selecting multi-year periods. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarDate, type DateValue } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const format = (date: DateValue) => date.year.toString() const parse = (string: string | undefined) => { if (!string) return const fullRegex = /^(\d{4})$/ const fullMatch = string.match(fullRegex) if (fullMatch) { const [_, year] = fullMatch.map(Number) return new CalendarDate(year, 1, 1) } } export const YearPickerRange = () => { return ( Label Clear {(context) => ( <> {context().getDecade().start} - {context().getDecade().end} {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Select Today Use the `selectToday` method from the date picker context to add a "Today" button that quickly selects the current date. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const SelectToday = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Fixed Weeks Use the `fixedWeeks` prop to always display 6 weeks in the calendar, preventing layout shifts when navigating between months. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const FixedWeeks = () => { return ( Label Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )} ) } ``` ### Form Use the `name` prop to integrate the date picker with native HTML forms. The selected date value will be submitted as form data. This example also uses `isDateUnavailable` to disable weekends. ```tsx import { DatePicker } from '@ark-ui/solid/date-picker' import { isWeekend } from '@internationalized/date' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' export const Form = () => { return (
{ e.preventDefault() const formData = new FormData(e.currentTarget) const date = formData.get('date') alert(`Selected date: ${date}`) }} > Appointment date Clear {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} {(context) => ( <> {(months) => ( {(month) => ( {month().label} )} )} )} {(context) => ( <> {(years) => ( {(year) => ( {year().label} )} )} )}
) } ``` ### With Time Integrate a time input with the date picker using `CalendarDateTime` from `@internationalized/date`. The time input updates the hour and minute of the selected date value. ```tsx import { CalendarDateTime, DateFormatter, getLocalTimeZone } from '@internationalized/date' import { DatePicker, type DatePickerValueChangeDetails } from '@ark-ui/solid/date-picker' import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid' import { Index, createSignal } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/date-picker.module.css' const formatter = new DateFormatter('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', }) export const WithTime = () => { const [value, setValue] = createSignal([new CalendarDateTime(2025, 1, 29, 14, 30)]) const timeValue = () => { const v = value()[0] return v ? `${String(v.hour).padStart(2, '0')}:${String(v.minute).padStart(2, '0')}` : '' } const onTimeChange = (e: Event & { currentTarget: HTMLInputElement }) => { const [hours, minutes] = e.currentTarget.value.split(':').map(Number) setValue((prev) => { const current = prev[0] ?? new CalendarDateTime(2025, 1, 1, 0, 0) return [current.set({ hour: hours, minute: minutes })] }) } const onDateChange = (details: DatePickerValueChangeDetails) => { const newDate = details.value[0] if (!newDate) return setValue([]) const prevTime = value()[0] ?? { hour: 0, minute: 0 } setValue([new CalendarDateTime(newDate.year, newDate.month, newDate.day, prevTime.hour, prevTime.minute)]) } return ( Date and time {value()[0] ? formatter.format(value()[0].toDate(getLocalTimeZone())) : 'Select date and time'} {(context) => ( <> {(weekDay) => ( {weekDay().short} )} {(week) => ( {(day) => ( {day().day} )} )} )} ) } ``` ## API Reference ### Props ### Root #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`closeOnSelect`** Type: `boolean` Required: false Default Value: `true` Description: Whether the calendar should close after the date selection is complete. This is ignored when the selection mode is `multiple`. **`createCalendar`** Type: `(identifier: CalendarIdentifier) => Calendar` Required: false Default Value: `undefined` Description: A function that creates a Calendar object for a given calendar identifier. Enables non-Gregorian calendar support (Persian, Buddhist, Islamic, etc.) without bundling all calendars by default. **`defaultFocusedValue`** Type: `DateValue` Required: false Default Value: `undefined` Description: The initial focused date when rendered. Use when you don't need to control the focused date of the date picker. **`defaultOpen`** Type: `boolean` Required: false Default Value: `undefined` Description: The initial open state of the date picker when rendered. Use when you don't need to control the open state of the date picker. **`defaultValue`** Type: `DateValue[]` Required: false Default Value: `undefined` Description: The initial selected date(s) when rendered. Use when you don't need to control the selected date(s) of the date picker. **`defaultView`** Type: `DateView` Required: false Default Value: `"day"` Description: The default view of the calendar **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the calendar is disabled. **`fixedWeeks`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the calendar should have a fixed number of weeks. This renders the calendar with 6 weeks instead of 5 or 6. **`focusedValue`** Type: `DateValue` Required: false Default Value: `undefined` Description: The controlled focused date. **`format`** Type: `(date: DateValue, details: LocaleDetails) => string` Required: false Default Value: `undefined` Description: The format of the date to display in the input. **`ids`** Type: `Partial<{ root: string; label: (index: number) => string; table: (id: string) => string; tableHeader: (id: string) => string; tableBody: (id: string) => string; tableRow: (id: string) => string; content: string; ... 10 more ...; positioner: string; }>` Required: false Default Value: `undefined` Description: The ids of the elements in the date picker. Useful for composition. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`inline`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to render the date picker inline **`invalid`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date picker is invalid **`isDateUnavailable`** Type: `(date: DateValue, locale: string) => boolean` Required: false Default Value: `undefined` Description: Returns whether a date of the calendar is available. **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`locale`** Type: `string` Required: false Default Value: `"en-US"` Description: The locale (BCP 47 language tag) to use when formatting the date. **`max`** Type: `DateValue` Required: false Default Value: `undefined` Description: The maximum date that can be selected. **`maxSelectedDates`** Type: `number` Required: false Default Value: `undefined` Description: The maximum number of dates that can be selected. This is only applicable when `selectionMode` is `multiple`. **`maxView`** Type: `DateView` Required: false Default Value: `"year"` Description: The maximum view of the calendar **`min`** Type: `DateValue` Required: false Default Value: `undefined` Description: The minimum date that can be selected. **`minView`** Type: `DateView` Required: false Default Value: `"day"` Description: The minimum view of the calendar **`name`** Type: `string` Required: false Default Value: `undefined` Description: The `name` attribute of the input element. **`numOfMonths`** Type: `number` Required: false Default Value: `undefined` Description: The number of months to display. **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`onFocusChange`** Type: `(details: FocusChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the focused date changes. **`onOpenChange`** Type: `(details: OpenChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the calendar opens or closes. **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the value changes. **`onViewChange`** Type: `(details: ViewChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the view changes. **`onVisibleRangeChange`** Type: `(details: VisibleRangeChangeDetails) => void` Required: false Default Value: `undefined` Description: Function called when the visible range changes. **`open`** Type: `boolean` Required: false Default Value: `undefined` Description: The controlled open state of the date picker **`openOnClick`** Type: `boolean` Required: false Default Value: `false` Description: Whether to open the calendar when the input is clicked. **`outsideDaySelectable`** Type: `boolean` Required: false Default Value: `false` Description: Whether day outside the visible range can be selected. **`parse`** Type: `(value: string, details: LocaleDetails) => DateValue | undefined` Required: false Default Value: `undefined` Description: Function to parse the date from the input back to a DateValue. **`placeholder`** Type: `string` Required: false Default Value: `undefined` Description: The placeholder text to display in the input. **`positioning`** Type: `PositioningOptions` Required: false Default Value: `undefined` Description: The user provided options used to position the date picker content **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`readOnly`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the calendar is read-only. **`required`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the date picker is required **`selectionMode`** Type: `SelectionMode` Required: false Default Value: `"single"` Description: The selection mode of the calendar. - `single` - only one date can be selected - `multiple` - multiple dates can be selected - `range` - a range of dates can be selected **`showWeekNumbers`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to show the week number column in the day view. **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`startOfWeek`** Type: `number` Required: false Default Value: `undefined` Description: The first day of the week. `0` - Sunday `1` - Monday `2` - Tuesday `3` - Wednesday `4` - Thursday `5` - Friday `6` - Saturday **`timeZone`** Type: `string` Required: false Default Value: `"UTC"` Description: The time zone to use **`translations`** Type: `Partial` Required: false Default Value: `undefined` Description: The localized messages to use. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. **`value`** Type: `DateValue[]` Required: false Default Value: `undefined` Description: The controlled selected date(s). **`view`** Type: `DateView` Required: false Default Value: `undefined` Description: The view of the calendar #### Data Attributes **`data-scope`**: date-picker **`data-part`**: root **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-empty`**: Present when the content is empty ### ClearTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Content #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: content **`data-state`**: "open" | "closed" **`data-nested`**: popover **`data-has-nested`**: popover **`data-placement`**: The placement of the content **`data-side`**: The side of the trigger that the content is positioned on **`data-inline`**: Present when the content is inline ### Control #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: control **`data-disabled`**: Present when disabled **`data-placeholder-shown`**: Present when placeholder is shown **`data-invalid`**: Present when invalid ### Input #### Props **`asChild`** Type: `(props: ParentProps<'input'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`fixOnBlur`** Type: `boolean` Required: false Default Value: `true` Description: Whether to fix the input value on blur. **`index`** Type: `number` Required: false Default Value: `undefined` Description: The index of the input to focus. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: input **`data-index`**: The index of the item **`data-state`**: "open" | "closed" **`data-placeholder-shown`**: Present when placeholder is shown **`data-invalid`**: Present when invalid ### Label #### Props **`asChild`** Type: `(props: ParentProps<'label'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: label **`data-state`**: "open" | "closed" **`data-index`**: The index of the item **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid ### MonthSelect #### Props **`asChild`** Type: `(props: ParentProps<'select'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### NextTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: next-trigger **`data-disabled`**: Present when disabled ### Positioner #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### PresetTrigger #### Props **`value`** Type: `PresetTriggerValue` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### PrevTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: prev-trigger **`data-disabled`**: Present when disabled ### RangeText #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### RootProvider #### Props **`value`** Type: `UseDatePickerReturn` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`immediate`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether to synchronize the present change immediately or defer it to the next frame **`lazyMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to enable lazy mounting **`onExitComplete`** Type: `VoidFunction` Required: false Default Value: `undefined` Description: Function called when the animation ends in the closed state **`present`** Type: `boolean` Required: false Default Value: `undefined` Description: Whether the node is present (controlled by the user) **`skipAnimationOnMount`** Type: `boolean` Required: false Default Value: `false` Description: Whether to allow the initial presence animation. **`unmountOnExit`** Type: `boolean` Required: false Default Value: `false` Description: Whether to unmount on exit. ### TableBody #### Props **`asChild`** Type: `(props: ParentProps<'tbody'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: table-body **`data-view`**: The view of the tablebody **`data-disabled`**: Present when disabled ### TableCell #### Props **`value`** Type: `number | DateValue` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'td'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`columns`** Type: `number` Required: false Default Value: `undefined` Description: undefined **`disabled`** Type: `boolean` Required: false Default Value: `undefined` Description: undefined **`visibleRange`** Type: `VisibleRange` Required: false Default Value: `undefined` Description: undefined ### TableCellTrigger #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### TableHead #### Props **`asChild`** Type: `(props: ParentProps<'thead'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: table-head **`data-view`**: The view of the tablehead **`data-disabled`**: Present when disabled ### TableHeader #### Props **`asChild`** Type: `(props: ParentProps<'th'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: table-header **`data-view`**: The view of the tableheader **`data-disabled`**: Present when disabled ### Table #### Props **`asChild`** Type: `(props: ParentProps<'table'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`columns`** Type: `number` Required: false Default Value: `undefined` Description: undefined #### Data Attributes **`data-scope`**: date-picker **`data-part`**: table **`data-columns`**: **`data-view`**: The view of the table ### TableRow #### Props **`asChild`** Type: `(props: ParentProps<'tr'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: table-row **`data-disabled`**: Present when disabled **`data-view`**: The view of the tablerow ### Trigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: trigger **`data-placement`**: The placement of the trigger **`data-side`**: The side of the trigger that the trigger is positioned on **`data-state`**: "open" | "closed" **`data-placeholder-shown`**: Present when placeholder is shown ### ValueText #### Props **`asChild`** Type: `(props: ParentProps<'span'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. **`placeholder`** Type: `string` Required: false Default Value: `undefined` Description: Text to display when no date is selected. **`separator`** Type: `string` Required: false Default Value: `", "` Description: The separator to use between multiple date values when using default rendering. ### ValueTextRender #### Props **`index`** Type: `number` Required: true Default Value: `undefined` Description: undefined **`remove`** Type: `() => void` Required: true Default Value: `undefined` Description: undefined **`value`** Type: `DateValue` Required: true Default Value: `undefined` Description: undefined **`valueAsString`** Type: `string` Required: true Default Value: `undefined` Description: undefined ### ViewControl #### Props **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: view-control **`data-view`**: The view of the viewcontrol ### View #### Props **`view`** Type: `DateView` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'div'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: view **`data-view`**: The view of the view ### ViewTrigger #### Props **`asChild`** Type: `(props: ParentProps<'button'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: view-trigger **`data-view`**: The view of the viewtrigger **`data-disabled`**: Present when disabled ### WeekNumberCell #### Props **`week`** Type: `DateValue[]` Required: true Default Value: `undefined` Description: undefined **`weekIndex`** Type: `number` Required: true Default Value: `undefined` Description: undefined **`asChild`** Type: `(props: ParentProps<'td'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: week-number-cell **`data-view`**: The view of the weeknumbercell **`data-week-index`**: **`data-type`**: The type of the item **`data-disabled`**: Present when disabled ### WeekNumberHeaderCell #### Props **`asChild`** Type: `(props: ParentProps<'th'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. #### Data Attributes **`data-scope`**: date-picker **`data-part`**: week-number-header-cell **`data-view`**: The view of the weeknumberheadercell **`data-type`**: The type of the item **`data-disabled`**: Present when disabled ### YearSelect #### Props **`asChild`** Type: `(props: ParentProps<'select'>) => Element` Required: false Default Value: `undefined` Description: Use the provided child element as the default rendered element, combining their props and behavior. ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `focused` | `boolean` | Whether the input is focused | | `open` | `boolean` | Whether the date picker is open | | `disabled` | `boolean` | Whether the date picker is disabled | | `invalid` | `boolean` | Whether the date picker is invalid | | `readOnly` | `boolean` | Whether the date picker is read-only | | `inline` | `boolean` | Whether the date picker is rendered inline | | `numOfMonths` | `number` | The number of months to display | | `showWeekNumbers` | `boolean` | Whether the week number column is shown in the day view | | `selectionMode` | `SelectionMode` | The selection mode (single, multiple, or range) | | `maxSelectedDates` | `number | undefined` | The maximum number of dates that can be selected (only for multiple selection mode). | | `isMaxSelected` | `boolean` | Whether the maximum number of selected dates has been reached. | | `view` | `DateView` | The current view of the date picker | | `getWeekNumber` | `(week: DateValue[]) => number` | Returns the ISO 8601 week number (1-53) for the given week (array of dates). | | `getDaysInWeek` | `(week: number, from?: DateValue) => DateValue[]` | Returns an array of days in the week index counted from the provided start date, or the first visible date if not given. | | `getOffset` | `(duration: DateDuration) => DateValueOffset` | Returns the offset of the month based on the provided number of months. | | `getRangePresetValue` | `(value: DateRangePreset) => DateValue[]` | Returns the range of dates based on the provided date range preset. | | `getMonthWeeks` | `(from?: DateValue) => DateValue[][]` | Returns the weeks of the month from the provided date. Represented as an array of arrays of dates. | | `isUnavailable` | `(date: DateValue) => boolean` | Returns whether the provided date is available (or can be selected) | | `weeks` | `DateValue[][]` | The weeks of the month. Represented as an array of arrays of dates. | | `weekDays` | `WeekDay[]` | The days of the week. Represented as an array of strings. | | `visibleRange` | `VisibleRange` | The visible range of dates. | | `visibleRangeText` | `VisibleRangeText` | The human readable text for the visible range of dates. | | `value` | `DateValue[]` | The selected date. | | `valueAsDate` | `Date[]` | The selected date as a Date object. | | `valueAsString` | `string[]` | The selected date as a string. | | `focusedValue` | `DateValue` | The focused date. | | `focusedValueAsDate` | `Date` | The focused date as a Date object. | | `focusedValueAsString` | `string` | The focused date as a string. | | `selectToday` | `VoidFunction` | Sets the selected date to today. | | `setValue` | `(values: DateValue[]) => void` | Sets the selected date to the given date. | | `setTime` | `(time: Time, index?: number) => void` | Sets the time for a specific date value. Converts CalendarDate to CalendarDateTime if needed. | | `setFocusedValue` | `(value: DateValue) => void` | Sets the focused date to the given date. | | `clearValue` | `(options?: { focus?: boolean }) => void` | Clears the selected date(s). | | `setOpen` | `(open: boolean) => void` | Function to open or close the calendar. | | `focusMonth` | `(month: number) => void` | Function to set the selected month. | | `focusYear` | `(year: number) => void` | Function to set the selected year. | | `getYears` | `() => Cell[]` | Returns the months of the year | | `getYearsGrid` | `(props?: YearGridProps) => YearGridValue` | Returns the years of the decade based on the columns. Represented as an array of arrays of years. | | `getDecade` | `() => Range` | Returns the start and end years of the decade. | | `getMonths` | `(props?: MonthFormatOptions) => Cell[]` | Returns the months of the year | | `getMonthsGrid` | `(props?: MonthGridProps) => MonthGridValue` | Returns the months of the year based on the columns. Represented as an array of arrays of months. | | `format` | `(value: DateValue, opts?: Intl.DateTimeFormatOptions) => string` | Formats the given date value based on the provided options. | | `setView` | `(view: DateView) => void` | Sets the view of the date picker. | | `goToNext` | `VoidFunction` | Goes to the next month/year/decade. | | `goToPrev` | `VoidFunction` | Goes to the previous month/year/decade. | | `getDayTableCellState` | `(props: DayTableCellProps) => DayTableCellState` | Returns the state details for a given cell. | | `getMonthTableCellState` | `(props: TableCellProps) => TableCellState` | Returns the state details for a given month cell. | | `getYearTableCellState` | `(props: TableCellProps) => TableCellState` | Returns the state details for a given year cell. | ## Accessibility ### Keyboard Support **`ArrowLeft`** Description: Moves focus to the previous day within the current week. **`ArrowRight`** Description: Moves focus to the next day within the current week. **`ArrowUp`** Description: Moves focus to the same day of the week in the previous week. **`ArrowDown`** Description: Moves focus to the same day of the week in the next week. **`Home`** Description: Moves focus to the first day of the current month. **`End`** Description: Moves focus to the last day of the current month. **`PageUp`** Description: Moves focus to the same day of the month in the previous month. **`PageDown`** Description: Moves focus to the same day of the month in the next month. **`Enter`** Description: Selects the focused date and closes the date picker. **`Esc`** Description: Closes the date picker without selecting any date. # Dialog ## Anatomy ```tsx ``` ## Examples ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const Basic = () => ( Open Dialog Welcome Back Sign in to your account to continue. ) ``` ### Controlled Manage the dialog state using the `open` and `onOpenChange` props. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const Controlled = () => { const [open, setOpen] = createSignal(false) return ( setOpen(e.open)}> Open Dialog Session Settings Manage your session preferences and security options. ) } ``` ### Root Provider An alternative way to control the dialog is to use the `RootProvider` component and the `useDialog` hook. This way you can access the state and methods from outside the component. ```tsx import { Dialog, useDialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const RootProvider = () => { const dialog = useDialog() return ( <> Account Settings Manage your account preferences and security options. ) } ``` ### Alert Dialog For critical confirmations or destructive actions, use `role="alertdialog"`. Alert dialogs differ from regular dialogs in important ways: - **Automatic focus**: The close/cancel button receives focus when opened, prioritizing the safest action - **Requires explicit dismissal**: Cannot be closed by clicking outside, only via button clicks or Escape key ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const AlertDialog = () => ( Delete Account Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers.
Cancel
) ``` ### Lazy Mount Use `lazyMount` to render dialog content only when first opened. Combine with `unmountOnExit` to unmount when closed, freeing up resources. Prefer this over conditionally rendering `Dialog.Root`—see **Conditional Rendering**. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const LazyMount = () => ( Open Dialog Lazy Mounted Dialog This dialog content is only mounted when opened and unmounted when closed. ) ``` ### Initial Focus Use `initialFocusEl` to control which element receives focus when the dialog opens. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const InitialFocus = () => { let inputRef: HTMLInputElement | undefined return ( inputRef!}> Open Dialog Edit Profile The name input will be focused when this dialog opens.
) } ``` ### Final Focus Use `finalFocusEl` to control which element receives focus when the dialog closes. Defaults to the trigger element. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const FinalFocus = () => { let buttonRef: HTMLButtonElement | undefined return ( <> buttonRef!}> Open Dialog Custom Focus Return When this dialog closes, focus will return to the "Focus Target" button instead of the trigger. ) } ``` ### Non-Modal Use `modal={false}` to allow interaction with elements outside the dialog. Disables focus trapping and scroll prevention. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const NonModal = () => ( Open Non-Modal Non-Modal Dialog This dialog allows interaction with elements outside while open. ) ``` ### Inside Scroll Make the content area scrollable while keeping header and footer fixed using `maxHeight` and `overflow: auto`. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const InsideScroll = () => ( Open Dialog Terms of Service Please review our terms before continuing.
{CONTENT_SECTIONS.map((item) => (

{item.title}

{item.body}

))}
Decline Accept
) const CONTENT_SECTIONS = [ { title: '1. Acceptance of Terms', body: 'By accessing and using this service, you accept and agree to be bound by the terms and provisions of this agreement.', }, { title: '2. Use License', body: 'Permission is granted to temporarily use this service for personal, non-commercial purposes only. This is the grant of a license, not a transfer of title.', }, { title: '3. User Responsibilities', body: 'You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.', }, { title: '4. Privacy Policy', body: 'Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.', }, { title: '5. Limitations', body: 'In no event shall we be liable for any damages arising out of the use or inability to use the materials on this service.', }, { title: '6. Revisions', body: 'We may revise these terms of service at any time without notice. By using this service you are agreeing to be bound by the then current version of these terms.', }, { title: '7. Governing Law', body: 'These terms and conditions are governed by and construed in accordance with applicable laws and you irrevocably submit to the exclusive jurisdiction of the courts.', }, ] ``` ### Outside Scroll Make the positioner scrollable so the dialog can extend beyond the viewport. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const OutsideScroll = () => { let contentRef: HTMLDivElement | undefined return ( contentRef!}> Open Dialog Privacy Policy This layout allows the dialog to extend beyond the viewport while keeping the outer container scrollable.
{CONTENT_SECTIONS.map((item) => (

{item.title}

{item.body}

))}
) } const CONTENT_SECTIONS = [ { title: '1. Information We Collect', body: 'We collect information you provide directly, such as when you create an account, make a purchase, or contact us for support. This may include your name, email address, and payment information.', }, { title: '2. How We Use Your Information', body: 'We use the information we collect to provide and improve our services, process transactions, send communications, and personalize your experience.', }, { title: '3. Information Sharing', body: 'We do not sell your personal information. We may share information with service providers who assist in our operations, or when required by law.', }, { title: '4. Data Security', body: 'We implement appropriate technical and organizational measures to protect your personal information against unauthorized access, alteration, or destruction.', }, { title: '5. Your Rights', body: 'You have the right to access, correct, or delete your personal information. You may also opt out of marketing communications at any time.', }, { title: '6. Cookies and Tracking', body: 'We use cookies and similar technologies to enhance your experience, analyze usage patterns, and deliver targeted content. You can manage cookie preferences in your browser settings.', }, { title: '7. Third-Party Services', body: 'Our service may contain links to third-party websites. We are not responsible for the privacy practices of these external sites.', }, { title: '8. Children Privacy', body: 'Our services are not directed to children under 13. We do not knowingly collect personal information from children without parental consent.', }, { title: '9. International Transfers', body: 'Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for such transfers.', }, { title: '10. Changes to This Policy', body: 'We may update this privacy policy from time to time. We will notify you of significant changes by posting a notice on our website or sending you an email.', }, { title: '11. Data Retention', body: 'We retain your personal information for as long as necessary to fulfill the purposes outlined in this policy, unless a longer retention period is required by law.', }, { title: '12. Contact Us', body: 'If you have questions about this privacy policy or our data practices, please contact our privacy team through the support channels provided on our website.', }, ] ``` ### Context Access the dialog's state and methods with `Dialog.Context` or the `useDialogContext` hook. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const Context = () => ( Open Dialog Status {(dialog) => Dialog is {dialog().open ? 'open' : 'closed'}} ) ``` ### Open from Menu Open a dialog imperatively from a menu item using the `onClick` handler. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { Menu } from '@ark-ui/solid/menu' import { ChevronDownIcon, XIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import { Portal } from 'solid-js/web' import dialog from 'styles/dialog.module.css' import menu from 'styles/menu.module.css' export const OpenFromMenu = () => { const [open, setOpen] = createSignal(false) return ( <> Actions Edit Duplicate setOpen(true)}> Delete... setOpen(e.open)} role="alertdialog"> Confirm Delete Are you sure you want to delete this item? This action cannot be undone. ) } ``` ### Nested Nest dialogs within one another. The parent receives `data-has-nested` and `--nested-layer-count` CSS variable for styling effects like zoom-out: ```css [data-part='content'][data-has-nested] { transform: scale(calc(1 - var(--nested-layer-count) * 0.05)); } ``` ```tsx import { Dialog, useDialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const Nested = () => { const parentDialog = useDialog() const childDialog = useDialog() return ( <> Parent Dialog This is the parent dialog. Open a nested dialog from here.
Nested Dialog This is a nested dialog with proper z-index layering. ) } ``` ### Multiple Triggers Share a single dialog across multiple trigger elements. Pass a `value` to each `Dialog.Trigger` and use `onTriggerValueChange` to update the dialog content based on which trigger was clicked. ```tsx import { Dialog } from '@ark-ui/solid/dialog' import { Portal } from 'solid-js/web' import { XIcon } from 'lucide-solid' import { For, Show, createSignal } from 'solid-js' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' import field from 'styles/field.module.css' interface User { id: string name: string email: string } const users: User[] = [ { id: '1', name: 'Alice Johnson', email: 'alice@example.com' }, { id: '2', name: 'Bob Smith', email: 'bob@example.com' }, { id: '3', name: 'Carol Davis', email: 'carol@example.com' }, ] export const MultipleTriggers = () => { const [activeUser, setActiveUser] = createSignal(null) return ( { setActiveUser(users.find((u) => u.id === e.value) ?? null) }} >
{(user) => ( Edit {user.name} )}
Edit User Update the user's information below. {(user) => ( <>
Cancel Save Changes
)}
) } ``` ### Confirmation Intercept close attempts to show confirmation prompts, preventing data loss from unsaved changes. ```tsx import { Dialog, useDialog } from '@ark-ui/solid/dialog' import { XIcon } from 'lucide-solid' import { createSignal } from 'solid-js' import { Portal } from 'solid-js/web' import button from 'styles/button.module.css' import styles from 'styles/dialog.module.css' export const Confirmation = () => { const [formContent, setFormContent] = createSignal('') const parentDialog = useDialog({ onOpenChange: (details) => { if (!details.open && formContent().trim()) { confirmDialog().setOpen(true) } }, }) const confirmDialog = useDialog() const handleConfirmClose = () => { confirmDialog().setOpen(false) parentDialog().setOpen(false) setFormContent('') } return ( <> Edit Content Make changes to your content. You'll be asked to confirm if you have unsaved changes.