` element and displays a custom cursor element during scrubbing interactions.
This component utilizes the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) for
smooth dragging interactions.
> **Note:** Browsers may show a notification when the Pointer Lock API is activated. The scrubber is automatically
> disabled in Safari to prevent layout shifts.
### Controlled
When controlling the NumberInput component, it's recommended to use string values instead of converting to numbers. This
is especially important when using `formatOptions` for currency or locale-specific formatting.
```tsx
const [value, setValue] = useState('0')
setValue(details.value)}>
{/* ... */}
```
Converting values to numbers can cause issues with locale-specific formatting, particularly for currencies that use
different decimal and thousands separators (e.g., `1.523,30` vs `1,523.30`). By keeping values as strings, you preserve
the correct formatting and avoid parsing issues.
If you need to submit a numeric value in your form, use a hidden input that reads `valueAsNumber` from
`NumberInput.Context`:
```tsx
setValue(details.value)}>
{(context) => }
```
## API Reference
### Props
### Root
#### Props
**`allowMouseWheel`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow mouse wheel to change the value
**`allowOverflow`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to allow the value overflow the min/max range
**`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.
**`clampValueOnBlur`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to clamp the value when the input loses focus (blur)
**`defaultValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial value of the input when rendered.
Use when you don't need to control the value of the input.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is disabled.
**`focusInputOnChange`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to focus input when the value changes
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the input element.
**`formatOptions`**
Type: `NumberFormatOptions`
Required: false
Default Value: `undefined`
Description: The options to pass to the `Intl.NumberFormat` constructor
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
root: string
label: string
input: string
incrementTrigger: string
decrementTrigger: string
scrubber: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the number input. Useful for composition.
**`inputMode`**
Type: `InputMode`
Required: false
Default Value: `"decimal"`
Description: Hints at the type of data that might be entered by the user. It also determines
the type of keyboard shown to the user on mobile devices
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input value is invalid.
**`largeStep`**
Type: `number`
Required: false
Default Value: `10 * step`
Description: The amount to increment or decrement the value by when the `Shift` key is held.
**`locale`**
Type: `string`
Required: false
Default Value: `"en-US"`
Description: The current locale. Based on the BCP 47 definition.
**`max`**
Type: `number`
Required: false
Default Value: `Number.MAX_SAFE_INTEGER`
Description: The maximum value of the number input
**`min`**
Type: `number`
Required: false
Default Value: `Number.MIN_SAFE_INTEGER`
Description: The minimum value of the number input
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name attribute of the number input. Useful for form submission.
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the number input is focused
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value changes
**`onValueCommit`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value is committed (when the input is blurred or the Enter key is pressed)
**`onValueInvalid`**
Type: `(details: ValueInvalidDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value overflows or underflows the min/max range
**`pattern`**
Type: `string`
Required: false
Default Value: `"-?[0-9]*(.[0-9]+)?"`
Description: The pattern used to check the
element's value against
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is readonly
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the number input is required
**`smallStep`**
Type: `number`
Required: false
Default Value: `step / 10`
Description: The amount to increment or decrement the value by when the `Alt` key is held.
**`spinOnPress`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to spin the value when the increment/decrement button is pressed
**`step`**
Type: `number`
Required: false
Default Value: `1`
Description: The amount to increment or decrement the value by
**`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 input
#### Data Attributes
**`data-scope`**: number-input
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-invalid`**: Present when invalid
**`data-scrubbing`**:
### 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`**: number-input
**`data-part`**: control
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-scrubbing`**:
### DecrementTrigger
#### 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`**: number-input
**`data-part`**: decrement-trigger
**`data-disabled`**: Present when disabled
**`data-scrubbing`**:
### IncrementTrigger
#### 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`**: number-input
**`data-part`**: increment-trigger
**`data-disabled`**: Present when disabled
**`data-scrubbing`**:
### 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`**: number-input
**`data-part`**: input
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-scrubbing`**:
### 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`**: number-input
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`data-scrubbing`**:
### RootProvider
#### Props
**`value`**
Type: `UseNumberInputReturn`
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.
### Scrubber
#### 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`**: number-input
**`data-part`**: scrubber
**`data-disabled`**: Present when disabled
**`data-scrubbing`**:
### 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.
#### Data Attributes
**`data-scope`**: number-input
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
**`data-scrubbing`**:
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `focused` | `boolean` | Whether the input is focused. |
| `invalid` | `boolean` | Whether the input is invalid. |
| `empty` | `boolean` | Whether the input value is empty. |
| `value` | `string` | The formatted value of the input. |
| `valueAsNumber` | `number` | The value of the input as a number. |
| `setValue` | `(value: number) => void` | Function to set the value of the input. |
| `clearValue` | `VoidFunction` | Function to clear the value of the input. |
| `increment` | `VoidFunction` | Function to increment the value of the input by the step. |
| `decrement` | `VoidFunction` | Function to decrement the value of the input by the step. |
| `setToMax` | `VoidFunction` | Function to set the value of the input to the max. |
| `setToMin` | `VoidFunction` | Function to set the value of the input to the min. |
| `focus` | `VoidFunction` | Function to focus the input. |
## Accessibility
Complies with the [Spinbutton WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).
### Keyboard Support
**`ArrowUp`**
Description: Increments the value of the number input by a predefined step.
**`ArrowDown`**
Description: Decrements the value of the number input by a predefined step.
**`Shift + ArrowUp`**
Description: Increments the value of the number input by the `largeStep` amount.
**`Shift + ArrowDown`**
Description: Decrements the value of the number input by the `largeStep` amount.
**`Alt + ArrowUp`**
Description: Increments the value of the number input by the `smallStep` amount.
**`Alt + ArrowDown`**
Description: Decrements the value of the number input by the `smallStep` amount.
**`Home`**
Description: Sets the value of the number input to its minimum allowed value.
**`End`**
Description: Sets the value of the number input to its maximum allowed value.
**`Enter`**
Description: Submits the value entered in the number input.
# Pagination
## Anatomy
```tsx
```
## Examples
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const Basic = () => (
{(pagination) => (
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
)}
)
```
### Controlled
To create a controlled Pagination component, manage the state of the current page using the `page` prop and update it
when the `onPageChange` event handler is called:
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For, createSignal } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const Controlled = () => {
const [currentPage, setCurrentPage] = createSignal(1)
return (
setCurrentPage(details.page)}
class={styles.Root}
>
{(pagination) => (
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
)}
)
}
```
### Root Provider
An alternative way to control the pagination is to use the `RootProvider` component and the `usePagination` hook. This
way you can access the state and methods from outside the component.
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination, usePagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const RootProvider = () => {
const pagination = usePagination({ count: 5000, pageSize: 10, siblingCount: 2 })
return (
pagination().goToNextPage()}>
Next Page
{(pagination) => (
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
)}
)
}
```
### Customization
You can customize the Pagination component by setting various props such as `dir`, `pageSize`, `siblingCount`, and
`translations`. Here's an example of a customized Pagination:
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const Customized = () => {
return (
`Page ${details.page}`,
}}
class={styles.Root}
>
{(pagination) => (
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
)}
)
}
```
### Context
Access pagination state and methods with `Pagination.Context` or the `usePaginationContext` hook. You get methods like
`setPage`, `setPageSize`, `goToNextPage`, `goToPrevPage`, `goToFirstPage`, `goToLastPage`, as well as properties like
`totalPages` and `pageRange`.
```tsx
import { ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import styles from 'styles/pagination.module.css'
export const Context = () => {
return (
{(pagination) => (
pagination().goToFirstPage()}>
pagination().goToPrevPage()}>
Page {pagination().page} of {pagination().totalPages}
pagination().goToNextPage()}>
pagination().goToLastPage()}>
)}
)
}
```
### Data Slicing
Use the `slice()` method to paginate actual data arrays. This method automatically slices your data based on the current
page and page size.
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
const users = [
{ id: 1, name: 'Emma Wilson', email: 'emma@example.com' },
{ id: 2, name: 'Liam Johnson', email: 'liam@example.com' },
{ id: 3, name: 'Olivia Brown', email: 'olivia@example.com' },
{ id: 4, name: 'Noah Davis', email: 'noah@example.com' },
{ id: 5, name: 'Ava Martinez', email: 'ava@example.com' },
{ id: 6, name: 'Ethan Garcia', email: 'ethan@example.com' },
{ id: 7, name: 'Sophia Rodriguez', email: 'sophia@example.com' },
{ id: 8, name: 'Mason Lee', email: 'mason@example.com' },
{ id: 9, name: 'Isabella Walker', email: 'isabella@example.com' },
{ id: 10, name: 'James Hall', email: 'james@example.com' },
{ id: 11, name: 'Mia Allen', email: 'mia@example.com' },
{ id: 12, name: 'Benjamin Young', email: 'benjamin@example.com' },
{ id: 13, name: 'Charlotte King', email: 'charlotte@example.com' },
{ id: 14, name: 'William Wright', email: 'william@example.com' },
{ id: 15, name: 'Amelia Scott', email: 'amelia@example.com' },
{ id: 16, name: 'Henry Green', email: 'henry@example.com' },
{ id: 17, name: 'Harper Adams', email: 'harper@example.com' },
{ id: 18, name: 'Sebastian Baker', email: 'sebastian@example.com' },
{ id: 19, name: 'Evelyn Nelson', email: 'evelyn@example.com' },
{ id: 20, name: 'Jack Carter', email: 'jack@example.com' },
]
export const DataSlicing = () => {
return (
{(pagination) => (
<>
{(user) => (
{user.name}
{user.email}
)}
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
>
)}
)
}
```
### Page Range
Display the current page range information using the `pageRange` property. This shows which items are currently visible
(e.g., "Showing 1-10 of 100 results").
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const PageRange = () => {
return (
{(pagination) => (
<>
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
Showing {pagination().pageRange.start + 1}-{pagination().pageRange.end} of {pagination().count} results
Page {pagination().page} of {pagination().totalPages}
>
)}
)
}
```
### Page Size
Control the number of items per page dynamically using `setPageSize()`. This example shows how to integrate a native
select element to change the page size.
> **Note:** For uncontrolled behavior, use `defaultPageSize` to set the initial value. For controlled behavior, use
> `pageSize` and `onPageSizeChange` to programmatically manage the page size.
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const PageSizeControl = () => {
return (
{(pagination) => (
<>
Items per page:
pagination().setPageSize(Number(e.target.value))}
>
5
10
20
50
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
Page {pagination().page} of {pagination().totalPages}
>
)}
)
}
```
### Links
Create pagination with link navigation for better SEO and accessibility. This example shows how to use the pagination
component with anchor links instead of buttons.
```tsx
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Pagination, usePagination } from '@ark-ui/solid/pagination'
import { For } from 'solid-js'
import styles from 'styles/pagination.module.css'
export const Link = () => {
const pagination = usePagination({
type: 'link',
count: 100,
pageSize: 10,
siblingCount: 2,
getPageUrl: ({ page }) => `/page=${page}`,
})
return (
{(page, index) =>
page.type === 'page' ? (
{page.value}
) : (
…
)
}
)
}
```
## API Reference
### Props
### Root
#### Props
**`asChild`**
Type: `(props: ParentProps<'nav'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
**`boundaryCount`**
Type: `number`
Required: false
Default Value: `1`
Description: Number of pages to show at the beginning and end
**`count`**
Type: `number`
Required: false
Default Value: `undefined`
Description: Total number of data items
**`defaultPage`**
Type: `number`
Required: false
Default Value: `1`
Description: The initial active page when rendered.
Use when you don't need to control the active page of the pagination.
**`defaultPageSize`**
Type: `number`
Required: false
Default Value: `10`
Description: The initial number of data items per page when rendered.
Use when you don't need to control the page size of the pagination.
**`getPageUrl`**
Type: `(details: PageUrlDetails) => string`
Required: false
Default Value: `undefined`
Description: Function to generate href attributes for pagination links.
Only used when `type` is set to "link".
**`ids`**
Type: `Partial<{
root: string
ellipsis: (index: number) => string
firstTrigger: string
prevTrigger: string
nextTrigger: string
lastTrigger: string
item: (page: number) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the accordion. Useful for composition.
**`onPageChange`**
Type: `(details: PageChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the page number is changed
**`onPageSizeChange`**
Type: `(details: PageSizeChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the page size is changed
**`page`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled active page
**`pageSize`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled number of data items per page
**`siblingCount`**
Type: `number`
Required: false
Default Value: `1`
Description: Number of pages to show beside active page
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`type`**
Type: `'link' | 'button'`
Required: false
Default Value: `"button"`
Description: The type of the trigger element
### Ellipsis
#### Props
**`index`**
Type: `number`
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.
### FirstTrigger
#### 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`**: pagination
**`data-part`**: first-trigger
**`data-disabled`**: Present when disabled
### Item
#### Props
**`type`**
Type: `'page'`
Required: true
Default Value: `undefined`
Description: undefined
**`value`**
Type: `number`
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.
#### Data Attributes
**`data-scope`**: pagination
**`data-part`**: item
**`data-index`**: The index of the item
**`data-selected`**: Present when selected
### LastTrigger
#### 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`**: pagination
**`data-part`**: last-trigger
**`data-disabled`**: Present when disabled
### 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`**: pagination
**`data-part`**: next-trigger
**`data-disabled`**: Present when disabled
### 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`**: pagination
**`data-part`**: prev-trigger
**`data-disabled`**: Present when disabled
### RootProvider
#### Props
**`value`**
Type: `UsePaginationReturn`
Required: true
Default Value: `undefined`
Description: undefined
**`asChild`**
Type: `(props: ParentProps<'nav'>) => 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 page. |
| `count` | `number` | The total number of data items. |
| `pageSize` | `number` | The number of data items per page. |
| `totalPages` | `number` | The total number of pages. |
| `pages` | `Pages` | The page range. Represented as an array of page numbers (including ellipsis) |
| `previousPage` | `number | null` | The previous page. |
| `nextPage` | `number | null` | The next page. |
| `pageRange` | `PageRange` | The page range. Represented as an object with `start` and `end` properties. |
| `slice` | `
(data: V[]) => V[]` | Function to slice an array of data based on the current page. |
| `setPageSize` | `(size: number) => void` | Function to set the page size. |
| `setPage` | `(page: number) => void` | Function to set the current page. |
| `goToNextPage` | `VoidFunction` | Function to go to the next page. |
| `goToPrevPage` | `VoidFunction` | Function to go to the previous page. |
| `goToFirstPage` | `VoidFunction` | Function to go to the first page. |
| `goToLastPage` | `VoidFunction` | Function to go to the last page. |
# Password Input
## Anatomy
```tsx
```
## Examples
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import styles from 'styles/password-input.module.css'
export const Basic = () => (
Password
}>
)
```
### Autocomplete
Use the `autoComplete` prop to manage autocompletion in the input.
- `new-password` — The user is creating a new password.
- `current-password` — The user is entering an existing password.
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import styles from 'styles/password-input.module.css'
export const Autocomplete = () => (
Password
}>
)
```
### Controlled Visibility
Use the `visible` and `onVisibilityChange` props to control the visibility of the password input.
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/password-input.module.css'
export const ControlledVisibility = () => {
const [visible, setVisible] = createSignal(false)
return (
setVisible(e.visible)}>
Password is {visible() ? 'visible' : 'hidden'}
}>
)
}
```
### Root Provider
An alternative way to control the password input is to use the `RootProvider` component and the `usePasswordInput` hook.
This way you can access the state and methods from outside the component.
```tsx
import { PasswordInput, usePasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import styles from 'styles/password-input.module.css'
export const RootProvider = () => {
const passwordInput = usePasswordInput()
return (
password input is {passwordInput().visible ? 'visible' : 'hidden'}
Password
}>
)
}
```
### Field
Here's an example of how to use the `PasswordInput` component with the `Field` component.
```tsx
import { Field } from '@ark-ui/solid/field'
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import field from 'styles/field.module.css'
import styles from 'styles/password-input.module.css'
export const WithField = () => (
Password
}>
Enter your password
Password is required
)
```
### Password Managers
Use the `ignorePasswordManager` prop to ignore password managers like 1Password, LastPass, etc. This is useful for
non-login scenarios (e.g., "api keys", "secure notes", "temporary passwords")
> **Currently, this only works for 1Password, LastPass, Bitwarden, Dashlane, and Proton Pass.**
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import styles from 'styles/password-input.module.css'
export const IgnorePasswordManager = () => (
API Key
}>
)
```
### Strength Meter
Combine the `PasswordInput` with a password strength library to show visual feedback about password strength. This
example uses the [`check-password-strength`](https://www.npmjs.com/package/check-password-strength) package to provide
real-time strength validation.
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { passwordStrength, type Options } from 'check-password-strength'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import { createMemo, createSignal, Show } from 'solid-js'
import styles from 'styles/password-input.module.css'
const strengthOptions: Options = [
{ id: 0, value: 'weak', minDiversity: 0, minLength: 0 },
{ id: 1, value: 'medium', minDiversity: 2, minLength: 6 },
{ id: 2, value: 'strong', minDiversity: 4, minLength: 8 },
]
export const StrengthMeter = () => {
const [password, setPassword] = createSignal('asdfasdf')
const strength = createMemo(() => {
if (!password()) return null
const { value } = passwordStrength(password(), strengthOptions)
return value
})
return (
Password
setPassword(e.currentTarget.value)}
placeholder="Enter your password"
/>
}>
{(value) => (
)}
)
}
```
### Validation
Combine with custom validation logic to show real-time feedback. Use the `invalid` prop to indicate validation errors.
```tsx
import { PasswordInput } from '@ark-ui/solid/password-input'
import { EyeIcon, EyeOffIcon } from 'lucide-solid'
import { createMemo, createSignal } from 'solid-js'
import styles from 'styles/password-input.module.css'
export const WithValidation = () => {
const [password, setPassword] = createSignal('')
const isValid = createMemo(() => password().length >= 8)
return (
0}>
Password (min 8 characters)
setPassword(e.currentTarget.value)}
placeholder="Enter your password"
/>
}>
{password().length > 0 && !isValid() && (
Password must be at least 8 characters
)}
{isValid() && password().length > 0 && (
Password is valid
)}
)
}
```
## 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.
**`autoComplete`**
Type: `'current-password' | 'new-password'`
Required: false
Default Value: `"current-password"`
Description: The autocomplete attribute for the password input.
**`defaultVisible`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The default visibility of the password input.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the password input is disabled.
**`ids`**
Type: `Partial<{ input: string; visibilityTrigger: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the password input parts
**`ignorePasswordManagers`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: When `true`, the input will ignore password managers.
**Only works for the following password managers**
- 1Password, LastPass, Bitwarden, Dashlane, Proton Pass
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The invalid state of the password input.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the password input.
**`onVisibilityChange`**
Type: `(details: VisibilityChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the visibility changes.
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the password input is read only.
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the password input is required.
**`translations`**
Type: `Partial<{ visibilityTrigger: ((visible: boolean) => string) | undefined }>`
Required: false
Default Value: `undefined`
Description: The localized messages to use.
**`visible`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the password input is visible.
#### Data Attributes
**`data-scope`**: password-input
**`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`**: password-input
**`data-part`**: control
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
### Indicator
#### 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 display when the password is not visible.
#### Data Attributes
**`data-scope`**: password-input
**`data-part`**: indicator
**`data-state`**: "visible" | "hidden"
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
### 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`**: password-input
**`data-part`**: input
**`data-state`**: "visible" | "hidden"
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`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`**: password-input
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UsePasswordInputReturn`
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.
### VisibilityTrigger
#### 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`**: password-input
**`data-part`**: visibility-trigger
**`data-readonly`**: Present when read-only
**`data-disabled`**: Present when disabled
**`data-state`**: "visible" | "hidden"
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `visible` | `boolean` | Whether the password input is visible. |
| `disabled` | `boolean` | Whether the password input is disabled. |
| `invalid` | `boolean` | Whether the password input is invalid. |
| `focus` | `VoidFunction` | Focus the password input. |
| `setVisible` | `(value: boolean) => void` | Set the visibility of the password input. |
| `toggleVisible` | `VoidFunction` | Toggle the visibility of the password input. |
# Pin Input
## Anatomy
```tsx
```
## Examples
```tsx
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const Basic = () => (
Label
{(id) => }
)
```
### Placeholder
To customize the default pin input placeholder `○` for each input, pass the placeholder prop and set it to your desired
value.
```tsx
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const CustomPlaceholder = () => (
Label
{(id) => }
)
```
### Blur on Complete
By default, the last input maintains focus when filled, and we invoke the `onValueComplete` callback. To blur the last
input when the user completes the input, set the prop `blurOnComplete` to `true`.
```tsx
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const BlurOnComplete = () => (
Label
{(id) => }
)
```
### OTP Mode
To trigger smartphone OTP auto-suggestion, it is recommended to set the `autocomplete` attribute to "one-time-code". The
pin input component provides support for this automatically when you set the `otp` prop to true.
```tsx
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const OTPMode = () => (
Label
{(id) => }
)
```
### Masking
When collecting private or sensitive information using the pin input, you might need to mask the value entered, similar
to ` `. Pass the `mask` prop to `true`.
```tsx
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const Mask = () => (
Label
{(id) => }
)
```
### Change Events
The pin input component invokes several callback functions when the user enters:
- `onValueChange` — Callback invoked when the value is changed.
- `onValueComplete` — Callback invoked when all fields have been completed (by typing or pasting).
- `onValueInvalid` — Callback invoked when an invalid value is entered into the input. An invalid value is any value
that doesn't match the specified "type".
### Field
The `Field` component helps manage form-related state and accessibility attributes of a pin input. It includes handling
ARIA labels, helper text, and error text to ensure proper accessibility.
```tsx
import { Field } from '@ark-ui/solid/field'
import { PinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import fieldStyles from 'styles/field.module.css'
import styles from 'styles/pin-input.module.css'
export const WithField = () => (
Label
{(id) => }
Additional Info
Error Info
)
```
### Root Provider
An alternative way to control the pin input is to use the `RootProvider` component and the `usePinInput` hook. This way
you can access the state and methods from outside the component.
```tsx
import { PinInput, usePinInput } from '@ark-ui/solid/pin-input'
import { Index } from 'solid-js'
import styles from 'styles/pin-input.module.css'
export const RootProvider = () => {
const pinInput = usePinInput({ onValueComplete: (e) => alert(e.valueAsString) })
return (
pinInput().focus()}>Focus
Label
{(id) => }
)
}
```
## 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.
**`autoFocus`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to auto-focus the first input.
**`autoSubmit`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to auto-submit the owning form when all inputs are filled.
**`blurOnComplete`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to blur the input when the value is complete
**`count`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The number of inputs to render to improve SSR aria attributes.
This will be required in next major version.
**`defaultValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial value of the the pin input when rendered.
Use when you don't need to control the value of the pin input.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the inputs are disabled
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input element.
**`ids`**
Type: `Partial<{
root: string
hiddenInput: string
label: string
control: string
input: (id: string) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the pin input. Useful for composition.
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the pin input is in the invalid state
**`mask`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the input's value will be masked just like `type=password`
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the input element. Useful for form submission.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called on input change
**`onValueComplete`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when all inputs have valid values
**`onValueInvalid`**
Type: `(details: ValueInvalidDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when an invalid value is entered
**`otp`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the pin input component signals to its fields that they should
use `autocomplete="one-time-code"`.
**`pattern`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The regular expression that the user-entered input value is checked against.
**`placeholder`**
Type: `string`
Required: false
Default Value: `"○"`
Description: The placeholder text for the input
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the pin input is in the valid state
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the pin input is required
**`sanitizeValue`**
Type: `(value: string) => string`
Required: false
Default Value: `undefined`
Description: Function to sanitize pasted values before validation.
Useful for stripping dashes, spaces, or other formatting.
**`selectOnFocus`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to select input value when input is focused
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`type`**
Type: `'numeric' | 'alphanumeric' | 'alphabetic'`
Required: false
Default Value: `"numeric"`
Description: The type of value the pin-input should allow
**`value`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled value of the the pin input.
#### Data Attributes
**`data-scope`**: pin-input
**`data-part`**: root
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-complete`**: Present when the pin-input value is complete
**`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.
### 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.
### Input
#### Props
**`index`**
Type: `number`
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.
#### Data Attributes
**`data-scope`**: pin-input
**`data-part`**: input
**`data-disabled`**: Present when disabled
**`data-complete`**: Present when the input value is complete
**`data-filled`**:
**`data-index`**: The index of the item
**`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`**: pin-input
**`data-part`**: label
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-complete`**: Present when the label value is complete
**`data-required`**: Present when required
**`data-readonly`**: Present when read-only
### RootProvider
#### Props
**`value`**
Type: `UsePinInputReturn`
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 |
|----------|------|-------------|
| `value` | `string[]` | The value of the input as an array of strings. |
| `valueAsString` | `string` | The value of the input as a string. |
| `complete` | `boolean` | Whether all inputs are filled. |
| `count` | `number` | The number of inputs to render |
| `items` | `number[]` | The array of input values. |
| `setValue` | `(value: string[]) => void` | Function to set the value of the inputs. |
| `clearValue` | `VoidFunction` | Function to clear the value of the inputs. |
| `setValueAtIndex` | `(index: number, value: string) => void` | Function to set the value of the input at a specific index. |
| `focus` | `VoidFunction` | Function to focus the pin-input. This will focus the first input. |
## Accessibility
### Keyboard Support
**`ArrowLeft`**
Description: Moves focus to the previous input
**`ArrowRight`**
Description: Moves focus to the next input
**`Backspace`**
Description: Deletes the value in the current input and moves focus to the previous input
**`Delete`**
Description: Deletes the value in the current input
**`Control + V`**
Description: Pastes the value into the input fields
# Popover
## Anatomy
```tsx
```
## Examples
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const Basic = () => (
Click Me
Favorite Frameworks
Manage and organize your favorite web frameworks.
)
```
### Controlled
Use the `open` and `onOpenChange` props to control the open state of the popover.
```tsx
import { Popover } from '@ark-ui/solid/popover'
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/popover.module.css'
export const Controlled = () => {
const [open, setOpen] = createSignal(false)
return (
setOpen(e.open)}>
Click Me
Team Members
Invite colleagues to collaborate on this project.
)
}
```
### Root Provider
An alternative way to control the popover is to use the `RootProvider` component and the `usePopover` hook. This way you
can access the state and methods from outside the component.
```tsx
import { Popover, usePopover } from '@ark-ui/solid/popover'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const RootProvider = () => {
const popover = usePopover()
return (
Popover is {popover().open ? 'open' : 'closed'}
Toggle Popover
Controlled Externally
This popover is controlled via the usePopover hook.
)
}
```
### Arrow
Use `Popover.Arrow` and `Popover.ArrowTip` to render an arrow pointing to the trigger.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const Arrow = () => (
Click Me
Notifications
You have 3 unread messages in your inbox.
)
```
### Placement
To change the placement of the popover, set the `positioning` prop.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const Positioning = () => (
Click Me
Left Placement
This popover appears on the left with custom offset values.
)
```
### Close Behavior
The popover is designed to close on blur and when the esc key is pressed.
- To prevent it from closing on blur (clicking or focusing outside), pass the `closeOnInteractOutside` prop and set it
to `false`.
- To prevent it from closing when the esc key is pressed, pass the `closeOnEsc` prop and set it to `false`.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const CloseBehavior = () => (
Click Me
Quick Actions
Press Escape or click outside to close this popover.
)
```
### Modality
In some cases, you might want the popover to be modal. This means that it'll:
- trap focus within its content
- block scrolling on the body
- disable pointer interactions outside the popover
- hide content behind the popover from screen readers
To make the popover modal, set the `modal` prop to `true`. When `modal={true}`, we set the `portalled` attribute to
`true` as well.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const Modal = () => (
Click Me
Confirm Action
Focus is trapped inside this modal popover until dismissed.
)
```
### Anchor
Use `Popover.Anchor` to position the popover relative to a different element than the trigger.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import field from 'styles/field.module.css'
import styles from 'styles/popover.module.css'
export const Anchor = () => (
Title
Description
)
```
### Same Width
Use `positioning.sameWidth` to make the popover match the width of its trigger element.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const SameWidth = () => (
Click Me
Matched Width
This popover matches the width of its trigger element.
)
```
### Dialog Integration
When rendering a popover inside a dialog, you have two options for proper layering:
1. **Keep the Portal with `lazyMount` and `unmountOnExit`** - This ensures the popover is properly unmounted when the
dialog closes, preventing stale DOM nodes.
2. **Remove the Portal** - Render the popover inline within the dialog content. This works well but may have z-index
considerations.
```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { Popover } from '@ark-ui/solid/popover'
import { 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/popover.module.css'
export const WithDialog = () => (
Open Dialog
Edit Profile
Update your profile information below.
More Options
Additional Settings
This popover renders correctly above the dialog.
)
```
### Nested
Popovers can be nested within each other. Each nested popover maintains its own open state and positioning.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
export const Nested = () => {
return (
Click Me
Settings
Manage your preferences and account settings.
Advanced
Advanced Settings
Configure advanced options for power users.
)
}
```
### Multiple Triggers
Share a single popover across multiple trigger elements. Pass a `value` to each `Popover.Trigger` — the popover
repositions to whichever trigger is activated without closing.
```tsx
import { Popover } from '@ark-ui/solid/popover'
import { Portal } from 'solid-js/web'
import { For, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/popover.module.css'
interface Item {
id: string
label: string
detail: string
}
const items: Item[] = [
{ id: 'share', label: 'Share', detail: 'Share this item with others via link or email.' },
{ id: 'export', label: 'Export', detail: 'Export this item as PDF, CSV, or JSON.' },
{ id: 'archive', label: 'Archive', detail: 'Move this item to the archive for later reference.' },
]
export const MultipleTriggers = () => {
const [activeItem, setActiveItem] = createSignal- (null)
return (
{
setActiveItem(items.find((i) => i.id === e.value) ?? null)
}}
>
{activeItem()?.label ?? 'Select an action'}
{activeItem()?.detail ?? 'Click a button above'}
)
}
```
## Guides
### Available Size
The following css variables are exposed to the `Popover.Positioner` which you can use to style the `Popover.Content`
```css
/* width of the popover trigger */
--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, use the following css:
```css
[data-scope='popover'][data-part='content'] {
max-height: calc(var(--available-height) - 100px);
}
```
## API Reference
### Props
### Root
#### Props
**`autoFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to automatically set focus on the first focusable
content within the popover when opened.
**`closeOnEscape`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to close the popover when the escape key is pressed.
**`closeOnInteractOutside`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to close the popover when the user clicks outside of the popover.
**`defaultOpen`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The initial open state of the popover when rendered.
Use when you don't need to control the open state of the popover.
**`defaultTriggerValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial trigger value when rendered.
Use when you don't need to control the trigger value.
**`finalFocusEl`**
Type: `() => MaybeElement`
Required: false
Default Value: `undefined`
Description: Element to receive focus when the popover is closed.
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
anchor: string
trigger: string | ((value?: string | undefined) => string)
content: string
title: string
description: string
closeTrigger: string
positioner: string
arrow: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the popover. 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 element to focus on when the popover is opened.
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`modal`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether the popover should be modal. When set to `true`:
- interaction with outside elements will be disabled
- only popover content will be visible to screen readers
- scrolling is blocked
- focus is trapped within the popover
**`onEscapeKeyDown`**
Type: `(event: KeyboardEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when the escape key is pressed
**`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
**`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 invoked when the popover opens or closes
**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when the pointer is pressed down outside the component
**`onRequestDismiss`**
Type: `(event: LayerDismissEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when this layer is closed due to a parent layer being closed
**`onTriggerValueChange`**
Type: `(details: TriggerValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the trigger value changes.
**`open`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The controlled open state of the popover
**`persistentElements`**
Type: `(() => Element | null)[]`
Required: false
Default Value: `undefined`
Description: Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event
**`portalled`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position
of the popover content.
**`positioning`**
Type: `PositioningOptions`
Required: false
Default Value: `undefined`
Description: The user provided options used to position the popover content
**`present`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the node is present (controlled by the user)
**`restoreFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to restore focus to the element that had focus before the popover was opened.
**`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
**`triggerValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled trigger value
**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.
### Anchor
#### 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.
### Arrow
#### 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.
### ArrowTip
#### 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.
### CloseTrigger
#### 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`**: popover
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: popover
**`data-has-nested`**: popover
**`data-expanded`**: Present when expanded
**`data-placement`**: The placement of the content
**`data-side`**: The side of the trigger that the content is positioned on
### Description
#### 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.
### 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`**: popover
**`data-part`**: indicator
**`data-state`**: "open" | "closed"
### 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: `UsePopoverReturn`
Required: true
Default Value: `undefined`
Description: undefined
**`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.
### Title
#### 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.
### 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.
**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The value that identifies this specific trigger
#### Data Attributes
**`data-scope`**: popover
**`data-part`**: trigger
**`data-placement`**: The placement of the trigger
**`data-side`**: The side of the trigger that the trigger is positioned on
**`data-value`**: The value of the item
**`data-current`**: Present when current
**`data-state`**: "open" | "closed"
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `portalled` | `boolean` | Whether the popover is portalled. |
| `open` | `boolean` | Whether the popover is open |
| `setOpen` | `(open: boolean) => void` | Function to open or close the popover |
| `triggerValue` | `string | null` | The trigger value |
| `setTriggerValue` | `(value: string | null) => void` | Function to set the trigger value |
| `reposition` | `(options?: Partial) => void` | Function to reposition the popover |
## Accessibility
### Keyboard Support
**`Space`**
Description: Opens/closes the popover.
**`Enter`**
Description: Opens/closes the popover.
**`Tab`**
Description: Moves focus to the next focusable element within the content.Note: If there are no focusable elements, focus is moved to the next focusable element after the trigger.
**`Shift + Tab`**
Description: Moves focus to the previous focusable element within the contentNote: If there are no focusable elements, focus is moved to the trigger.
**`Esc`**
Description: Closes the popover and moves focus to the trigger.
# Progress - Circular
## Anatomy
```tsx
```
## Examples
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress-circular.module.css'
export const Basic = () => (
)
```
### Min and Max
By default, the maximum is `100`. If that's not what you want, you can easily specify a different bound by changing the
value of the `max` prop. You can do the same with the minimum value by setting the `min` prop.
For example, to show the user a progress from `10` to `30`, you can use:
```tsx
import { Progress } from '@ark-ui/solid/progress'
export const MinMax = () => (
Label
)
```
### Indeterminate
The progress component is determinate by default, with the value and max set to 50 and 100 respectively. To render an
indeterminate progress, you will have to set the `value` to `null`.
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress-circular.module.css'
export const Indeterminate = () => (
)
```
### Label
Add a label to provide additional context for the progress indicator.
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress-circular.module.css'
export const WithLabel = () => (
Label
)
```
### Root Provider
An alternative way to control the progress is to use the `RootProvider` component and the `useProgress` hook. This way
you can access the state and methods from outside the component.
```tsx
import { Progress, useProgress } from '@ark-ui/solid/progress'
import button from 'styles/button.module.css'
import styles from 'styles/progress-circular.module.css'
export const RootProvider = () => {
const progress = useProgress()
return (
progress().setToMax()}>
Set to Max
)
}
```
## Guides
### Styling the Circle
The circular progress component uses CSS variables to control the size and thickness of the circle. Set these variables
on `Progress.Circle` to customize the appearance:
```css
[data-scope='progress'][data-part='circle'] {
--size: 120px;
--thickness: 10px;
}
```
| Variable | Description |
| ------------- | ------------------------------------ |
| `--size` | The width and height of the circle |
| `--thickness` | The stroke width of the circle track |
## API Reference
### 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: `50`
Description: The initial value of the progress bar when rendered.
Use when you don't need to control the value of the progress bar.
**`formatOptions`**
Type: `NumberFormatOptions`
Required: false
Default Value: `{ style: "percent" }`
Description: The options to use for formatting the value.
**`ids`**
Type: `Partial<{ root: string; track: string; label: string; circle: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the progress bar. Useful for composition.
**`locale`**
Type: `string`
Required: false
Default Value: `"en-US"`
Description: The locale to use for formatting the value.
**`max`**
Type: `number`
Required: false
Default Value: `100`
Description: The maximum allowed value of the progress bar.
**`min`**
Type: `number`
Required: false
Default Value: `0`
Description: The minimum allowed value of the progress bar.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback fired when the value changes.
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the element.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: The localized messages to use.
**`value`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled value of the progress bar.
#### Data Attributes
**`data-scope`**: progress
**`data-part`**: root
**`data-max`**:
**`data-value`**: The value of the item
**`data-state`**:
**`data-orientation`**: The orientation of the progress
### Circle
#### Props
**`asChild`**
Type: `(props: ParentProps<'svg'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
### CircleRange
#### Props
**`asChild`**
Type: `(props: ParentProps<'circle'>) => 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`**: progress
**`data-part`**: circle-range
**`data-state`**:
### CircleTrack
#### Props
**`asChild`**
Type: `(props: ParentProps<'circle'>) => 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`**: progress
**`data-part`**: circle-track
**`data-orientation`**: The orientation of the circletrack
### 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-scope`**: progress
**`data-part`**: label
**`data-orientation`**: The orientation of the label
### Range
#### 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`**: progress
**`data-part`**: range
**`data-orientation`**: The orientation of the range
**`data-state`**:
### RootProvider
#### Props
**`value`**
Type: `UseProgressReturn`
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.
### Track
#### 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.
### 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.
### View
#### Props
**`state`**
Type: `ProgressState`
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`**: progress
**`data-part`**: view
**`data-state`**:
## Accessibility
Complies with the [the progressbar role requirements.](https://w3c.github.io/aria/#progressbar).
# Progress - Linear
## Anatomy
```tsx
```
## Examples
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress.module.css'
export const Basic = () => (
Label
)
```
### Min and Max
By default, the maximum is `100`. If that's not what you want, you can easily specify a different bound by changing the
value of the `max` prop. You can do the same with the minimum value by setting the `min` prop.
For example, to show the user a progress from `10` to `30`, you can use:
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress.module.css'
export const MinMax = () => (
Label
)
```
### Indeterminate
The progress component is determinate by default, with the value and max set to 50 and 100 respectively. To render an
indeterminate progress, you will have to set the `value` to `null`.
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress.module.css'
export const Indeterminate = () => (
Label
)
```
### Value Text
Progress bars can only be interpreted by sighted users. To include a text description to support assistive technologies
like screen readers, use the `value` part in `translations`.
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress.module.css'
export const ValueText = () => (
Label
)
```
### Orientation
By default, the progress is assumed to be horizontal. To change the orientation to vertical, set the orientation
property in the machine's context to vertical.
> Don't forget to change the styles of the vertical progress by specifying its height
```tsx
import { Progress } from '@ark-ui/solid/progress'
import styles from 'styles/progress.module.css'
export const Vertical = () => (
Label
)
```
### Root Provider
An alternative way to control the progress is to use the `RootProvider` component and the `useProgress` hook. This way
you can access the state and methods from outside the component.
```tsx
import { Progress, useProgress } from '@ark-ui/solid/progress'
import button from 'styles/button.module.css'
import styles from 'styles/progress.module.css'
export const RootProvider = () => {
const progress = useProgress()
return (
progress().setToMax()}>
Set to Max
Label
)
}
```
## API Reference
### 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: `50`
Description: The initial value of the progress bar when rendered.
Use when you don't need to control the value of the progress bar.
**`formatOptions`**
Type: `NumberFormatOptions`
Required: false
Default Value: `{ style: "percent" }`
Description: The options to use for formatting the value.
**`ids`**
Type: `Partial<{ root: string; track: string; label: string; circle: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the progress bar. Useful for composition.
**`locale`**
Type: `string`
Required: false
Default Value: `"en-US"`
Description: The locale to use for formatting the value.
**`max`**
Type: `number`
Required: false
Default Value: `100`
Description: The maximum allowed value of the progress bar.
**`min`**
Type: `number`
Required: false
Default Value: `0`
Description: The minimum allowed value of the progress bar.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback fired when the value changes.
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the element.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: The localized messages to use.
**`value`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled value of the progress bar.
#### Data Attributes
**`data-scope`**: progress
**`data-part`**: root
**`data-max`**:
**`data-value`**: The value of the item
**`data-state`**:
**`data-orientation`**: The orientation of the progress
### Circle
#### Props
**`asChild`**
Type: `(props: ParentProps<'svg'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
### CircleRange
#### Props
**`asChild`**
Type: `(props: ParentProps<'circle'>) => 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`**: progress
**`data-part`**: circle-range
**`data-state`**:
### CircleTrack
#### Props
**`asChild`**
Type: `(props: ParentProps<'circle'>) => 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`**: progress
**`data-part`**: circle-track
**`data-orientation`**: The orientation of the circletrack
### 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-scope`**: progress
**`data-part`**: label
**`data-orientation`**: The orientation of the label
### Range
#### 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`**: progress
**`data-part`**: range
**`data-orientation`**: The orientation of the range
**`data-state`**:
### RootProvider
#### Props
**`value`**
Type: `UseProgressReturn`
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.
### Track
#### 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.
### 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.
### View
#### Props
**`state`**
Type: `ProgressState`
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`**: progress
**`data-part`**: view
**`data-state`**:
## Accessibility
Complies with the [the progressbar role requirements.](https://w3c.github.io/aria/#progressbar).
# QR Code
## Anatomy
```tsx
```
## Examples
```tsx
import { QrCode } from '@ark-ui/solid/qr-code'
import styles from 'styles/qr-code.module.css'
export const Basic = () => {
return (
)
}
```
### With Overlay
You can also add a logo or overlay to the QR code. This is useful when you want to brand the QR code.
```tsx
import { QrCode } from '@ark-ui/solid/qr-code'
import styles from 'styles/qr-code.module.css'
export const Overlay = () => {
return (
)
}
```
### Error Correction
In cases where the link is too long or the logo overlay covers a significant area, the error correction level can be
increased.
Use the `encoding.ecc` or `encoding.boostEcc` property to set the error correction level:
- `L`: Allows recovery of up to 7% data loss (default)
- `M`: Allows recovery of up to 15% data loss
- `Q`: Allows recovery of up to 25% data loss
- `H`: Allows recovery of up to 30% data loss
```tsx
import { QrCode } from '@ark-ui/solid/qr-code'
import { RadioGroup } from '@ark-ui/solid/radio-group'
import { For, createSignal } from 'solid-js'
import styles from 'styles/qr-code.module.css'
import radio from 'styles/radio-group.module.css'
type ErrorLevel = 'L' | 'M' | 'Q' | 'H'
export const ErrorCorrection = () => {
const [errorLevel, setErrorLevel] = createSignal('L')
return (
setErrorLevel(e.value as ErrorLevel)}
>
{(level) => (
{level}
)}
)
}
```
### Root Provider
An alternative way to control the QR code is to use the `RootProvider` component and the `useQrCode` hook. This way you
can access the state and methods from outside the component.
```tsx
import { QrCode, useQrCode } from '@ark-ui/solid/qr-code'
import styles from 'styles/qr-code.module.css'
export const RootProvider = () => {
const qrCode = useQrCode({ value: 'http://ark-ui.com' })
return (
{qrCode().value}
)
}
```
### Download
Use the `QrCode.DownloadTrigger` component to allow users to download the QR code as an image. Specify the `fileName`
and `mimeType` props for the downloaded file.
```tsx
import { QrCode } from '@ark-ui/solid/qr-code'
import button from 'styles/button.module.css'
import styles from 'styles/qr-code.module.css'
export const Download = () => {
return (
Download
)
}
```
## 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 encode when rendered.
Use when you don't need to control the value of the qr code.
**`encoding`**
Type: `QrCodeGenerateOptions`
Required: false
Default Value: `undefined`
Description: The qr code encoding options.
**`ids`**
Type: `Partial<{ root: string; frame: string; overlay: string }>`
Required: false
Default Value: `undefined`
Description: The element ids.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback fired when the value changes.
**`pixelSize`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The pixel size of the qr code.
**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled value to encode.
### DownloadTrigger
#### Props
**`fileName`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The name of the file.
**`mimeType`**
Type: `DataUrlType`
Required: true
Default Value: `undefined`
Description: The mime type of the image.
**`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.
**`quality`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The quality of the image.
### Frame
#### Props
**`asChild`**
Type: `(props: ParentProps<'svg'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
### Overlay
#### 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.
### Pattern
#### Props
**`asChild`**
Type: `(props: ParentProps<'path'>) => 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: `UseQrCodeReturn`
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 |
|----------|------|-------------|
| `value` | `string` | The value to encode. |
| `setValue` | `(value: string) => void` | Set the value to encode. |
| `getDataUrl` | `(type: DataUrlType, quality?: number) => Promise` | Returns the data URL of the qr code. Includes the overlay when present. |
# Radio Group
## Anatomy
```tsx
```
## Examples
```tsx
import { RadioGroup } from '@ark-ui/solid/radio-group'
import { For } from 'solid-js'
import styles from 'styles/radio-group.module.css'
export const Basic = () => {
const frameworks = ['React', 'Solid', 'Vue']
return (
Framework
{(framework) => (
{framework}
)}
)
}
```
### Initial Value
To set the radio group's initial value, set the `defaultValue` prop to the value of the radio item to be selected by
default.
```tsx
import { RadioGroup } from '@ark-ui/solid/radio-group'
import { For } from 'solid-js'
import styles from 'styles/radio-group.module.css'
export const InitialValue = () => {
const frameworks = ['React', 'Solid', 'Vue']
return (
Framework
{(framework) => (
{framework}
)}
)
}
```
### Controlled
For a controlled Radio Group, the state is managed using the `value` prop, and updates when the `onValueChange` event
handler is called:
```tsx
import { RadioGroup } from '@ark-ui/solid/radio-group'
import { For, createSignal } from 'solid-js'
import styles from 'styles/radio-group.module.css'
export const Controlled = () => {
const frameworks = ['React', 'Solid', 'Vue']
const [value, setValue] = createSignal(null)
return (
setValue(e.value)}>
Framework
{(framework) => (
{framework}
)}
)
}
```
### Root Provider
An alternative way to control the radio group is to use the `RootProvider` component and the `useRadioGroup` hook. This
way you can access the state and methods from outside the component.
```tsx
import { RadioGroup, useRadioGroup } from '@ark-ui/solid/radio-group'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/radio-group.module.css'
export const RootProvider = () => {
const frameworks = ['React', 'Solid', 'Vue']
const radioGroup = useRadioGroup({ defaultValue: 'React' })
return (
Framework
{(framework) => (
{framework}
)}
radioGroup().setValue('Solid')}>
Set to Solid
)
}
```
### Disabled
To make a radio group disabled, set the `disabled` prop to `true`.
```tsx
import { RadioGroup } from '@ark-ui/solid/radio-group'
import { For } from 'solid-js'
import styles from 'styles/radio-group.module.css'
export const Disabled = () => {
const frameworks = ['React', 'Solid', 'Vue']
return (
Framework
{(framework) => (
{framework}
)}
)
}
```
## Guides
### asChild
The `RadioGroup.Item` component renders as a `label` element by default. This ensures proper form semantics and
accessibility, as radio groups are form controls that require labels to provide meaningful context for users.
When using the `asChild` prop, you must **render a `label` element** as the direct child of `RadioGroup.Item` to
maintain valid HTML structure and accessibility compliance.
```tsx
// INCORRECT usage ❌
// CORRECT usage ✅
```
### Hidden Input
The `RadioGroup.ItemHiddenInput` component renders a hidden HTML input element that enables proper form submission and
integration with native form behaviors. This component is essential for the radio group to function correctly as it:
- Provides the underlying input element that browsers use for form submission
- Enables integration with form libraries and validation systems
- Ensures the radio group works with native form reset functionality
```tsx
// INCORRECT usage ❌
// CORRECT usage ✅
```
## 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 of the checked radio when rendered.
Use when you don't need to control the value of the radio group.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the radio group will be disabled
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input.
**`ids`**
Type: `Partial<{
root: string
label: string
indicator: string
item: (value: string) => string
itemLabel: (value: string) => string
itemControl: (value: string) => string
itemHiddenInput: (value: string) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the radio. Useful for composition.
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the radio group is marked as invalid.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the input fields in the radio
(Useful for form submission).
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called once a radio is checked
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `undefined`
Description: Orientation of the radio group
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the radio group is read-only
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the radio group is marked as required.
**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled value of the radio group
#### Data Attributes
**`data-scope`**: radio-group
**`data-part`**: root
**`data-orientation`**: The orientation of the radio-group
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### 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`**: radio-group
**`data-part`**: indicator
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the indicator
### ItemControl
#### 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`**: radio-group
**`data-part`**: item-control
**`data-active`**: Present when active or pressed
### ItemHiddenInput
#### 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.
### Item
#### Props
**`value`**
Type: `string`
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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
### 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.
### 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-scope`**: radio-group
**`data-part`**: label
**`data-orientation`**: The orientation of the label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseRadioGroupReturn`
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 |
|----------|------|-------------|
| `value` | `string | null` | The current value of the radio group |
| `setValue` | `(value: string) => void` | Function to set the value of the radio group |
| `clearValue` | `VoidFunction` | Function to clear the value of the radio group |
| `focus` | `VoidFunction` | Function to focus the radio group |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state details of a radio input |
## Accessibility
Complies with the [Radio WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).
### Keyboard Support
**`Tab`**
Description: Moves focus to either the checked radio item or the first radio item in the group.
**`Space`**
Description: When focus is on an unchecked radio item, checks it.
**`ArrowDown`**
Description: Moves focus and checks the next radio item in the group.
**`ArrowRight`**
Description: Moves focus and checks the next radio item in the group.
**`ArrowUp`**
Description: Moves focus to the previous radio item in the group.
**`ArrowLeft`**
Description: Moves focus to the previous radio item in the group.
# Rating Group
## Anatomy
```tsx
```
## Examples
```tsx
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const Basic = () => (
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
```
### Controlled
When using the `RatingGroup` component, you can use the `value` and `onValueChange` props to control the state.
```tsx
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index, createSignal } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const Controlled = () => {
const [value, setValue] = createSignal(0)
return (
setValue(details.value)}>
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
}
```
### Root Provider
An alternative way to control the rating group is to use the `RootProvider` component and the `useRatingGroup` hook.
This way you can access the state and methods from outside the component.
```tsx
import { RatingGroup, useRatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const RootProvider = () => {
const ratingGroup = useRatingGroup({ defaultValue: 3 })
return (
value: {ratingGroup().value}
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
}
```
### Field
The `Field` component helps manage form-related state and accessibility attributes of a rating group. It includes
handling ARIA labels, helper text, and error text to ensure proper accessibility.
```tsx
import { Field } from '@ark-ui/solid/field'
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import field from 'styles/field.module.css'
import styles from 'styles/rating-group.module.css'
export const WithField = () => (
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
Additional Info
Error Info
)
```
### Half Rating
Allow `0.5` value steps by setting the `allowHalf` prop to `true`. Ensure to render the correct icon if the `half` value
is set in the Rating components render callback.
```tsx
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const HalfStar = () => (
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
```
### Forms
To use the rating group within forms, pass the prop `name`. It will render a hidden input and ensure the value changes
get propagated to the form correctly.
```tsx
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const FormUsage = () => (
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
```
### Disabled
To make the rating group disabled, set the `disabled` prop to `true`.
```tsx
import { RatingGroup } from '@ark-ui/solid/rating-group'
import { StarIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/rating-group.module.css'
export const Disabled = () => (
Label
{(context) => (
{(item) => (
{(itemContext) => (
)}
)}
)}
)
```
## API Reference
### Props
### Root
#### Props
**`allowHalf`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow half stars.
**`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 rating.
**`count`**
Type: `number`
Required: false
Default Value: `5`
Description: The total number of ratings.
**`defaultValue`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The initial value of the rating when rendered.
Use when you don't need to control the value of the rating.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the rating is disabled.
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input element.
**`ids`**
Type: `Partial<{
root: string
label: string
hiddenInput: string
control: string
item: (id: string) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the rating. Useful for composition.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name attribute of the rating element (used in forms).
**`onHoverChange`**
Type: `(details: HoverChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to be called when the rating value is hovered.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to be called when the rating value changes.
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the rating is readonly.
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the rating is required.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`value`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled value of the rating
### 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`**: rating-group
**`data-part`**: control
**`data-readonly`**: Present when read-only
**`data-disabled`**: Present when disabled
### 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.
### Item
#### Props
**`index`**
Type: `number`
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`**: rating-group
**`data-part`**: item
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-checked`**: Present when checked
**`data-highlighted`**: Present when highlighted
**`data-half`**:
### 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`**: rating-group
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseRatingGroupReturn`
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 |
|----------|------|-------------|
| `setValue` | `(value: number) => void` | Sets the value of the rating group |
| `clearValue` | `VoidFunction` | Clears the value of the rating group |
| `hovering` | `boolean` | Whether the rating group is being hovered |
| `value` | `number` | The current value of the rating group |
| `hoveredValue` | `number` | The value of the currently hovered rating |
| `count` | `number` | The total number of ratings |
| `items` | `number[]` | The array of rating values. Returns an array of numbers from 1 to the max value. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a rating item |
## Accessibility
### Keyboard Support
**`ArrowRight`**
Description: Moves focus to the next star, increasing the rating value based on the `allowHalf` property.
**`ArrowLeft`**
Description: Moves focus to the previous star, decreasing the rating value based on the `allowHalf` property.
**`Enter`**
Description: Selects the focused star in the rating group.
# Scroll Area
## Anatomy
```tsx
```
## Required style
It's important to note that the scroll area requires the following styles on the `ScrollArea.Viewport` element to hide
the native scrollbar:
```css
[data-scope='scroll-area'][data-part='viewport'] {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
```
## Examples
### Basic
Create a basic scrollable area with custom scrollbar.
```tsx
import { ScrollArea } from '@ark-ui/solid/scroll-area'
import styles from 'styles/scroll-area.module.css'
export const Basic = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt
explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur
magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia
dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et
dolore magnam aliquam quaerat voluptatem.
)
```
### Horizontal
Configure the scroll area for horizontal scrolling only.
```tsx
import { ScrollArea } from '@ark-ui/solid/scroll-area'
import styles from 'styles/scroll-area.module.css'
export const Horizontal = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
)
```
### Both Directions
Enable scrolling in both horizontal and vertical directions.
```tsx
import { ScrollArea } from '@ark-ui/solid/scroll-area'
import styles from 'styles/scroll-area.module.css'
export const BothDirections = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem
aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni
dolores eos qui ratione voluptatem sequi nesciunt.
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti
atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique
sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga.
)
```
### Nested
Scroll areas can be nested within each other for complex layouts.
```tsx
import { ScrollArea } from '@ark-ui/solid/scroll-area'
import styles from 'styles/scroll-area.module.css'
export const Nested = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
This is a nested scroll area. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit
voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo.
)
```
## 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.
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{ root: string; viewport: string; content: string; scrollbar: string; thumb: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the scroll area elements
#### Data Attributes
**`data-scope`**: scroll-area
**`data-part`**: root
**`data-overflow-x`**: Present when the content overflows the x-axis
**`data-overflow-y`**: Present when the content overflows the y-axis
### 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`**: scroll-area
**`data-part`**: content
**`data-overflow-x`**: Present when the content overflows the x-axis
**`data-overflow-y`**: Present when the content overflows the y-axis
### Corner
#### 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`**: scroll-area
**`data-part`**: corner
**`data-hover`**: Present when hovered
**`data-state`**: "hidden" | "visible"
**`data-overflow-x`**: Present when the content overflows the x-axis
**`data-overflow-y`**: Present when the content overflows the y-axis
### RootProvider
#### Props
**`value`**
Type: `UseScrollAreaReturn`
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.
### Scrollbar
#### 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.
**`orientation`**
Type: `Orientation`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: scroll-area
**`data-part`**: scrollbar
**`data-orientation`**: The orientation of the scrollbar
**`data-scrolling`**: Present when scrolling
**`data-hover`**: Present when hovered
**`data-dragging`**: Present when in the dragging state
**`data-overflow-x`**: Present when the content overflows the x-axis
**`data-overflow-y`**: Present when the content overflows the y-axis
### 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`**: scroll-area
**`data-part`**: thumb
**`data-orientation`**: The orientation of the thumb
**`data-hover`**: Present when hovered
**`data-dragging`**: Present when in the dragging state
### Viewport
#### 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`**: scroll-area
**`data-part`**: viewport
**`data-at-top`**: Present when scrolled to the top edge
**`data-at-bottom`**: Present when scrolled to the bottom edge
**`data-at-left`**: Present when scrolled to the left edge
**`data-at-right`**: Present when scrolled to the right edge
**`data-overflow-x`**: Present when the content overflows the x-axis
**`data-overflow-y`**: Present when the content overflows the y-axis
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `isAtTop` | `boolean` | Whether the scroll area is at the top |
| `isAtBottom` | `boolean` | Whether the scroll area is at the bottom |
| `isAtLeft` | `boolean` | Whether the scroll area is at the left |
| `isAtRight` | `boolean` | Whether the scroll area is at the right |
| `hasOverflowX` | `boolean` | Whether the scroll area has horizontal overflow |
| `hasOverflowY` | `boolean` | Whether the scroll area has vertical overflow |
| `getScrollProgress` | `() => Point` | Get the scroll progress as values between 0 and 1 |
| `scrollToEdge` | `(details: ScrollToEdgeDetails) => void` | Scroll to the edge of the scroll area |
| `scrollTo` | `(details: ScrollToDetails) => void` | Scroll to specific coordinates |
| `getScrollbarState` | `(props: ScrollbarProps) => ScrollbarState` | Returns the state of the scrollbar |
# Segment Group
## Anatomy
```tsx
```
## Examples
```tsx
import { SegmentGroup } from '@ark-ui/solid/segment-group'
import { Index } from 'solid-js'
import styles from 'styles/segment-group.module.css'
export const Basic = () => {
const frameworks = ['React', 'Solid', 'Svelte', 'Vue']
return (
{(framework) => (
{framework()}
)}
)
}
```
### Controlled
To create a controlled SegmentGroup component, manage the current selected segment using the `value` prop and update it
when the `onValueChange` event handler is called:
```tsx
import { SegmentGroup } from '@ark-ui/solid/segment-group'
import { Index, createSignal } from 'solid-js'
import styles from 'styles/segment-group.module.css'
export const Controlled = () => {
const frameworks = ['React', 'Solid', 'Svelte', 'Vue']
const [value, setValue] = createSignal(null)
return (
setValue(e.value)}>
{(framework) => (
{framework()}
)}
)
}
```
### Root Provider
An alternative way to control the segment group is to use the `RootProvider` component and the `useSegmentGroup` hook.
This way you can access the state and methods from outside the component.
```tsx
import { SegmentGroup, useSegmentGroup } from '@ark-ui/solid/segment-group'
import { Index } from 'solid-js'
import styles from 'styles/segment-group.module.css'
export const RootProvider = () => {
const frameworks = ['React', 'Solid', 'Svelte', 'Vue']
const segmentGroup = useSegmentGroup({ defaultValue: 'React' })
return (
selected: {segmentGroup().value}
{(framework) => (
{framework()}
)}
)
}
```
### Disabled
To disable a segment, simply pass the `disabled` prop to the `SegmentGroup.Item` component:
```tsx
import { SegmentGroup } from '@ark-ui/solid/segment-group'
import { Index } from 'solid-js'
import styles from 'styles/segment-group.module.css'
export const Disabled = () => {
const frameworks = ['React', 'Solid', 'Svelte', 'Vue']
return (
{(framework) => (
{framework()}
)}
)
}
```
## API Reference
### 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 of the checked radio when rendered.
Use when you don't need to control the value of the segment group.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the segment group will be disabled
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input.
**`ids`**
Type: `Partial<{
root: string
label: string
indicator: string
item: (value: string) => string
itemLabel: (value: string) => string
itemControl: (value: string) => string
itemHiddenInput: (value: string) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the radio. Useful for composition.
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the segment group is marked as invalid.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the input fields in the radio
(Useful for form submission).
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called once a radio is checked
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `undefined`
Description: Orientation of the segment group
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the segment group is read-only
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the segment group is marked as required.
**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled value of the segment group
#### Data Attributes
**`data-scope`**: segment-group
**`data-part`**: root
**`data-orientation`**: The orientation of the segment-group
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### 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`**: segment-group
**`data-part`**: indicator
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the indicator
### ItemControl
#### 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`**: segment-group
**`data-part`**: item-control
**`data-active`**: Present when active or pressed
### ItemHiddenInput
#### 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.
### Item
#### Props
**`value`**
Type: `string`
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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
### 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.
### 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-scope`**: segment-group
**`data-part`**: label
**`data-orientation`**: The orientation of the label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseRadioGroupReturn`
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.
## Accessibility
Complies with the [Radio WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).
### Keyboard Support
**`Tab`**
Description: Moves focus to either the checked radio item or the first radio item in the group.
**`Space`**
Description: When focus is on an unchecked radio item, checks it.
**`ArrowDown`**
Description: Moves focus and checks the next radio item in the group.
**`ArrowRight`**
Description: Moves focus and checks the next radio item in the group.
**`ArrowUp`**
Description: Moves focus to the previous radio item in the group.
**`ArrowLeft`**
Description: Moves focus to the previous radio item in the group.
# Select
## Anatomy
```tsx
```
## Examples
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const Basic = () => {
return (
Framework
Frameworks
{(item) => (
{item().label}
✓
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` props to control the selected items.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Controlled = () => {
const [value, setValue] = createSignal([])
const collection = createListCollection- ({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
const handleValueChange = (details: Select.ValueChangeDetails
- ) => {
setValue(details.value)
}
return (
Framework
Frameworks
{(item) => (
{item().label}
✓
)}
)
}
```
### Root Provider
An alternative way to control the select is to use the `RootProvider` component and the `useSelect` hook. This way you
can access the state and methods from outside the component.
```tsx
import { Select, createListCollection, useSelect } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const RootProvider = () => {
const select = useSelect({ collection: frameworks })
return (
<>
selected: {JSON.stringify(select().value)}
Framework
Frameworks
{(item) => (
{item().label}
✓
)}
>
)
}
```
### Multiple
To enable `multiple` item selection:
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
export const Multiple = () => {
return (
Framework
Frameworks
{(item) => (
{item().label}
✓
)}
)
}
```
### Grouping
Grouping related options can be useful for organizing options into categories.
- Use the `groupBy` prop to configure the grouping of the items.
- Use the `collection.group()` method to get the grouped items.
- Use the `Select.ItemGroup` and `Select.ItemGroupLabel` components to render the grouped items.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
export const Grouping = () => {
return (
Framework
{([type, group]) => (
{type}
{(item) => (
{item.label}
✓
)}
)}
)
}
```
### Field
Use `Field` to manage form state, ARIA labels, helper text, and error text.
```tsx
import { Field } from '@ark-ui/solid/field'
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index } from 'solid-js/web'
import field from 'styles/field.module.css'
import styles from 'styles/select.module.css'
export const WithField = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
Label
{(item) => (
{item()}
✓
)}
Additional Info
Error Info
)
}
```
### Form Usage
Here's an example of integrating the `Select` component with a form library.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { createForm, getValue, setValue } from '@modular-forms/solid'
import { createMemo } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
],
})
export const FormLibrary = () => {
const [formStore, { Form, Field }] = createForm({
initialValues: { value: 'solid' },
})
const value = createMemo(() => getValue(formStore, 'value'))
return (
<>
Value is {value()}
>
)
}
```
### Async Loading
Here's an example of how to load the items asynchronously when the select is opened.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Match, Switch, createMemo, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
function loadData() {
return new Promise((resolve) => {
setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500)
})
}
export const Async = () => {
const [items, setItems] = createSignal(null)
const [loading, setLoading] = createSignal(false)
const [error, setError] = createSignal(null)
const collection = createMemo(() =>
createListCollection({
items: items() || [],
}),
)
const handleOpenChange = (details: Select.OpenChangeDetails) => {
if (details.open && items() === null) {
setLoading(true)
setError(null)
loadData()
.then((data) => setItems(data))
.catch((err) => setError(err))
.finally(() => setLoading(false))
}
}
return (
Framework
Loading...
Error: {error()?.message}
{(item) => (
{item()}
✓
)}
)
}
```
### Lazy Mount
Use `lazyMount` and `unmountOnExit` to control when content is mounted, improving performance.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
export const LazyMount = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Alpine'],
})
return (
Framework
Clear
Frameworks
{(item) => (
{item()}
✓
)}
)
}
```
### Select on Highlight
Here's an example of automatically selecting items when they are highlighted (hovered or navigated to with keyboard).
```tsx
import { Select, createListCollection, useSelect } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
export const SelectOnHighlight = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const select = useSelect({
collection,
onHighlightChange({ highlightedValue }) {
if (highlightedValue) {
select().selectValue(highlightedValue)
}
},
})
return (
Framework
Clear
Frameworks
{(item) => (
{item()}
✓
)}
)
}
```
### Max Selection
Here's an example of limiting the number of items that can be selected in a multiple select.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, createMemo, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const MAX_SELECTION = 2
const hasReachedMax = (value: string[]) => value.length >= MAX_SELECTION
export const MaxSelected = () => {
const [value, setValue] = createSignal([])
const collection = createMemo(() =>
createListCollection({
items: items.map((item) => ({
label: item,
value: item,
disabled: hasReachedMax(value()) && !value().includes(item),
})),
}),
)
const handleValueChange = (details: Select.ValueChangeDetails) => {
if (hasReachedMax(value()) && details.value.length > value().length) return
setValue(details.value)
}
return (
Framework
Frameworks
{(item) => (
{item().label}
✓
)}
)
}
```
### Select All
Use `selectAll()` from the select context to select all items at once.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
import button from 'styles/button.module.css'
const SelectAllButton = () => {
return (
{(api) => (
{
api().selectAll()
api().setOpen(false)
}}
>
Select All
)}
)
}
export const SelectAll = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
Framework
Clear
{(item) => (
{item()}
✓
)}
)
}
```
### Overflow
For selects with many items, use `positioning.fitViewport` to ensure the dropdown fits within the viewport. Combine with
a max-height on the content to enable scrolling.
```tsx
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: [
'Name 1',
'Name 2',
'Name 3',
'Name 4',
'Name 5',
'Name 6',
'Name 7',
'Name 8',
'Name 9',
'Name 10',
'Name 11',
'Name 12',
'Name 13',
'Name 14',
],
})
export const Overflow = () => (
Framework
Clear
Names
{(item) => (
{item()}
✓
)}
)
```
## Guides
### Type Safety
The `Select.RootComponent` type enables you to create typed wrapper components that maintain full type safety for
collection items.
```tsx
const Select: ArkSelect.RootComponent = (props) => {
return {/* ... */}
}
```
Use the wrapper with full type inference on `onValueChange` and other callbacks:
```tsx
const App = () => {
const collection = createListCollection({
initialItems: [
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
],
})
return (
{
// e.items is typed as Array<{ label: string, value: string }>
console.log(e.items)
}}
>
{/* ... */}
)
}
```
### Hidden Select
The `Select.HiddenSelect` component renders a native HTML `` element that's visually hidden but remains in the
DOM. This component is essential for:
- **Form submission**: Native form submission and serialization work seamlessly since the actual `` element
exists in the DOM
- **Browser auto-fill**: Browsers can properly auto-fill the select based on previously submitted form data
- **Progressive enhancement**: Forms remain functional even if JavaScript fails to load
```tsx
{/* Other Select components */}
```
The hidden select automatically syncs with the Select component's value, ensuring form data is always up-to-date.
### Virtualization
For virtualized lists with many items, avoid using `Select.HiddenSelect` as it renders an `` for every item in
the collection. Instead, create a lightweight hidden input:
```tsx
import { Select, useSelectContext } from '@ark-ui/react/select'
const SelectHiddenInput = ({ name }: { name: string }) => {
const select = useSelectContext()
return (
<>
{select.value.map((value) => (
))}
>
)
}
```
### Empty State
You can create an empty state component that displays when there are no items in the collection. Use the
`useSelectContext` hook to check the collection size:
```tsx
const SelectEmpty = (props: React.ComponentProps<'div'>) => {
const select = useSelectContext()
if (select.collection.size === 0) {
return
}
return null
}
```
Then use it within your Select content:
```tsx
No items to display
{/* Your items */}
```
### Available Size
The following css variables are exposed to the `Select.Positioner` which you can use to style the `Select.Content`
```css
/* width of the select trigger */
--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='select'][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
**`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.
**`autoComplete`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The autocomplete attribute for the hidden select. Enables browser autofill (e.g. "address-level1" for state).
**`closeOnSelect`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the select should close after an item is selected
**`composite`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the select is a composed with other composite widgets like tabs or combobox
**`defaultHighlightedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial value of the highlighted item when opened.
Use when you don't need to control the highlighted value of the select.
**`defaultOpen`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select's open state is controlled by the user
**`defaultValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial default value of the select when rendered.
Use when you don't need to control the value of the select.
**`deselectable`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the value can be cleared by clicking the selected item.
**Note:** this is only applicable for single selection
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is disabled
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying select.
**`highlightedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled key of the highlighted item
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
root: string
content: string
control: string
trigger: string
clearTrigger: string
label: string
hiddenSelect: string
positioner: string
item: (id: string | number) => string
itemGroup: (id: string | number) => string
itemGroupLabel: (id: string | number) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the select. 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
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is invalid
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`loopFocus`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to loop the keyboard navigation through the options
**`multiple`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow multiple selection
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The `name` attribute of the underlying select.
**`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: The callback fired when the highlighted item 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: The callback fired when the selected item changes.
**`open`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select menu is open
**`positioning`**
Type: `PositioningOptions`
Required: false
Default Value: `undefined`
Description: The positioning options of 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 select is read-only
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is required
**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to scroll to a specific index
**`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 keys of the selected items
#### Data Attributes
**`data-scope`**: select
**`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`**: select
**`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`**: select
**`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-activedescendant`**: The id the active descendant of the content
### 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`**: select
**`data-part`**: control
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
### HiddenSelect
#### 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.
### 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`**: select
**`data-part`**: indicator
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
### 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`**: select
**`data-part`**: item-group
**`data-disabled`**: Present when disabled
### 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`**: select
**`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`**: select
**`data-part`**: item
**`data-value`**: The value of the item
**`data-state`**: "checked" | "unchecked"
**`data-highlighted`**: Present when highlighted
**`data-disabled`**: Present when disabled
### 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`**: select
**`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`**: select
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-required`**: Present when required
### 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.
### 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: `UseSelectReturn`
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.
#### Data Attributes
**`data-scope`**: select
**`data-part`**: trigger
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-placement`**: The placement of the trigger
**`data-side`**: The side of the trigger that the trigger is positioned on
**`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 value is selected.
#### Data Attributes
**`data-scope`**: select
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `focused` | `boolean` | Whether the select is focused |
| `open` | `boolean` | Whether the select is open |
| `empty` | `boolean` | Whether the select value is empty |
| `highlightedValue` | `string | null` | The value of the highlighted item |
| `highlightedItem` | `V | null` | The highlighted item |
| `setHighlightValue` | `(value: string) => void` | Function to highlight a value |
| `clearHighlightValue` | `VoidFunction` | Function to clear the highlighted value |
| `selectedItems` | `V[]` | The selected items |
| `hasSelectedItems` | `boolean` | Whether there's a selected option |
| `value` | `string[]` | The selected item keys |
| `valueAsString` | `string` | The string representation of the selected items |
| `selectValue` | `(value: string) => void` | Function to select a value |
| `selectAll` | `VoidFunction` | Function to select all values |
| `setValue` | `(value: string[]) => void` | Function to set the value of the select |
| `clearValue` | `(value?: string) => void` | Function to clear the value of the select.
If a value is provided, it will only clear that value, otherwise, it will clear all values. |
| `focus` | `VoidFunction` | Function to focus on the select input |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a select item |
| `setOpen` | `(open: boolean) => void` | Function to open or close the select |
| `collection` | `ListCollection` | Function to toggle the select |
| `reposition` | `(options?: Partial) => void` | Function to set the positioning options of the select |
| `multiple` | `boolean` | Whether the select allows multiple selections |
| `disabled` | `boolean` | Whether the select is disabled |
## Accessibility
Complies with the [Listbox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/).
### Keyboard Support
**`Space`**
Description: When focus is on trigger, opens the select and focuses the first selected item. When focus is on the content, selects the highlighted item.
**`Enter`**
Description: When focus is on trigger, opens the select and focuses the first selected item. When focus is on content, selects the focused item.
**`ArrowDown`**
Description: When focus is on trigger, opens the select. When focus is on content, moves focus to the next item.
**`ArrowUp`**
Description: When focus is on trigger, opens the select. When focus is on content, moves focus to the previous item.
**`Esc`**
Description: Closes the select and moves focus to trigger.
**`A-Z + a-z`**
Description: When focus is on trigger, selects the item whose label starts with the typed character. When focus is on the listbox, moves focus to the next item with a label that starts with the typed character.
# Signature Pad
## Anatomy
```tsx
```
## Examples
```tsx
import { SignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import styles from 'styles/signature-pad.module.css'
export const Basic = () => (
Sign below
)
```
### Controlled
Use the `paths` prop with `onDraw` (React/Solid), `v-model:paths` (Vue), or `bind:paths` (Svelte) to control the
signature pad externally. The example tracks the path count and can clear the signature from outside the component.
```tsx
import { SignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/signature-pad.module.css'
export const Controlled = () => {
const [paths, setPaths] = createSignal([])
return (
paths: {paths().length}
setPaths(details.paths)}>
Sign below
setPaths([])}>
Clear
)
}
```
### Image Preview
After the user draws a signature, you can display a preview of the signature as an image. This is useful when you want
to show the user a preview of the signature before saving it.
```tsx
import { SignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import { Show, createSignal } from 'solid-js'
import styles from 'styles/signature-pad.module.css'
export const ImagePreview = () => {
const [imageUrl, setImageUrl] = createSignal('')
return (
details.getDataUrl('image/png').then((url) => setImageUrl(url))}
>
Sign below
Image Preview
)
}
```
### Field
The `Field` component helps manage form-related state and accessibility attributes of a signature pad. It includes
handling ARIA labels, helper text, and error text to ensure proper accessibility.
```tsx
import { Field } from '@ark-ui/solid/field'
import { SignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import field from 'styles/field.module.css'
import styles from 'styles/signature-pad.module.css'
export const WithField = () => (
Label
Additional Info
Error Info
)
```
### Root Provider
An alternative way to control the signature pad is to use the `RootProvider` component and the `useSignaturePad` hook.
This way you can access the state and methods from outside the component.
```tsx
import { SignaturePad, useSignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import styles from 'styles/signature-pad.module.css'
export const RootProvider = () => {
const signaturePad = useSignaturePad()
return (
no of paths: {signaturePad().paths.length}
Sign below
)
}
```
## 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.
**`defaultPaths`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The default paths of the signature pad.
Use when you don't need to control the paths of the signature pad.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the signature pad is disabled.
**`drawing`**
Type: `DrawingOptions`
Required: false
Default Value: `'{ size: 2, simulatePressure: true }'`
Description: The drawing options.
**`ids`**
Type: `Partial<{ root: string; control: string; hiddenInput: string; label: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the signature pad elements. Useful for composition.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the signature pad. Useful for form submission.
**`onDraw`**
Type: `(details: DrawDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback when the signature pad is drawing or the committed paths change.
`paths` contains only committed strokes; use `currentPath` for the in-progress stroke.
**`onDrawEnd`**
Type: `(details: DrawEndDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback when the signature pad is done drawing.
**`paths`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled paths of the signature pad.
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the signature pad is read-only.
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the signature pad is required.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: The translations of the signature pad. Useful for internationalization.
#### Data Attributes
**`data-scope`**: signature-pad
**`data-part`**: root
**`data-disabled`**: Present when disabled
### 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.
### 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`**: signature-pad
**`data-part`**: control
**`data-disabled`**: Present when disabled
### Guide
#### 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`**: signature-pad
**`data-part`**: guide
**`data-disabled`**: Present when disabled
### HiddenInput
#### Props
**`value`**
Type: `string`
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.
### 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`**: signature-pad
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseSignaturePadReturn`
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.
### Segment
#### Props
**`asChild`**
Type: `(props: ParentProps<'svg'>) => 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 |
|----------|------|-------------|
| `empty` | `boolean` | Whether the signature pad is empty. |
| `drawing` | `boolean` | Whether the user is currently drawing. |
| `currentPath` | `string | null` | The current path being drawn. |
| `paths` | `string[]` | The paths of the signature pad. |
| `getDataUrl` | `(type: DataUrlType, quality?: number) => Promise` | Returns the data URL of the signature pad. |
| `clear` | `VoidFunction` | Clears the signature pad. |
# Slider
## Anatomy
```tsx
```
## Examples
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const Basic = () => {
return (
Label
)
}
```
### Range
You can add multiple thumbs to the slider by adding multiple `Slider.Thumb`
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const Range = () => {
return (
Label
)
}
```
### Marks
You can add marks to the slider track by using the `Slider.MarkerGroup` and `Slider.Marker` components.
Position the `Slider.Marker` components relative to the track by providing the `value` prop.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import { For } from 'solid-js'
import styles from 'styles/slider.module.css'
export const WithMarks = () => {
return (
Label
{(value) => (
{value}
)}
)
}
```
### Min and Max
By default, the minimum is `0` and the maximum is `100`. If that's not what you want, you can easily specify different
bounds by changing the values of the `min` and/or `max` props.
For example, to ask the user for a value between `-10` and `10`, you can use:
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const MinMax = () => {
return (
Label
)
}
```
### Granularity
By default, the granularity, is `1`, meaning that the value is always an integer. You can change the step attribute to
control the granularity.
For example, If you need a value between `5` and `10`, accurate to two decimal places, you should set the value of step
to `0.01`:
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const Step = () => {
return (
Label
)
}
```
### Change Events
When the slider value changes, the `onValueChange` and `onValueChangeEnd` callbacks are invoked. You can use this to set
up custom behaviors in your app.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const OnEvent = () => {
return (
console.log('onValueChange', details.value)}
onValueChangeEnd={(details) => console.log('onValueChangeEnd', details.value)}
class={styles.Root}
>
Label
)
}
```
### Orientation
By default, the slider is assumed to be horizontal. To change the orientation to vertical, set the orientation property
in the machine's context to vertical.
In this mode, the slider will use the arrow up and down keys to increment/decrement its value.
> Don't forget to change the styles of the vertical slider by specifying its height
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const Vertical = () => {
return (
Label
)
}
```
### Origin
By default, the slider's origin is at the start of the track. To change the origin to the center of the track, set the
`origin` prop to `center`.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const CenterOrigin = () => {
return (
Label
)
}
```
### Root Provider
An alternative way to control the slider is to use the `RootProvider` component and the `useSlider` hook. This way you
can access the state and methods from outside the component.
```tsx
import { Slider, useSlider } from '@ark-ui/solid/slider'
import button from 'styles/button.module.css'
import styles from 'styles/slider.module.css'
export const RootProvider = () => {
const slider = useSlider()
return (
<>
slider().focus()}>
Focus
Label
>
)
}
```
### Dragging Indicator
Use the `Slider.DraggingIndicator` component inside `Slider.Thumb` to show a visual indicator while the thumb is being
dragged.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const DraggingIndicator = () => {
return (
Label
)
}
```
### Thumb Overlap
Use the `minStepsBetweenThumbs` prop to prevent range slider thumbs from overlapping. This ensures a minimum gap between
thumbs, which is useful for price range filters and similar use cases.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const ThumbOverlap = () => {
return (
Label
)
}
```
### Thumb Collision
Use the `thumbCollisionBehavior` prop to control how thumbs behave when they collide during pointer interactions.
Supported values are `push` (default), `swap`, and `none`.
```tsx
import { Slider } from '@ark-ui/solid/slider'
import styles from 'styles/slider.module.css'
export const ThumbCollision = () => {
return (
Label
)
}
```
## API Reference
### Props
### Root
#### Props
**`aria-label`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The aria-label of each slider thumb. Useful for providing an accessible name to the slider
**`aria-labelledby`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The `id` of the elements that labels each slider thumb. Useful for providing an accessible name to the slider
**`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: `undefined`
Description: The initial value of the slider when rendered.
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
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input element.
**`getAriaValueText`**
Type: `(details: ValueTextDetails) => string`
Required: false
Default Value: `undefined`
Description: Function that returns a human readable value for the slider thumb
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
root: string
thumb: (index: number) => string
hiddenInput: (index: number) => string
control: string
track: string
range: string
label: string
valueText: string
marker: (index: number) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the slider. Useful for composition.
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the slider is invalid
**`largeStep`**
Type: `number`
Required: false
Default Value: `10 * step`
Description: The step value of the slider when the `Shift` key is held, or the
`PageUp`/`PageDown` keys are used.
**`max`**
Type: `number`
Required: false
Default Value: `100`
Description: The maximum value of the slider
**`min`**
Type: `number`
Required: false
Default Value: `0`
Description: The minimum value of the slider
**`minStepsBetweenThumbs`**
Type: `number`
Required: false
Default Value: `0`
Description: The minimum permitted steps between multiple thumbs.
`minStepsBetweenThumbs` * `step` should reflect the gap between the thumbs.
- `step: 1` and `minStepsBetweenThumbs: 10` => gap is `10`
- `step: 10` and `minStepsBetweenThumbs: 2` => gap is `20`
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name associated with each slider thumb (when used in a form)
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the slider's focused index changes
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the value of the slider changes
**`onValueChangeEnd`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the slider value change is done
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the slider
**`origin`**
Type: `'start' | 'center' | 'end'`
Required: false
Default Value: `"start"`
Description: The origin of the slider range. The track is filled from the origin
to the thumb for single values.
- "start": Useful when the value represents an absolute value
- "center": Useful when the value represents an offset (relative)
- "end": Useful when the value represents an offset from the end
**`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 of the slider
**`thumbAlignment`**
Type: `'center' | 'contain'`
Required: false
Default Value: `"contain"`
Description: The alignment of the slider thumb relative to the track
- `center`: the thumb will extend beyond the bounds of the slider track.
- `contain`: the thumb will be contained within the bounds of the track.
**`thumbCollisionBehavior`**
Type: `'none' | 'push' | 'swap'`
Required: false
Default Value: `"none"`
Description: Controls how thumbs behave when they collide during pointer interactions.
- `none` (default): Thumbs cannot move past each other; excess movement is ignored.
- `push`: Thumbs push each other without restoring their previous positions when dragged back.
- `swap`: Thumbs swap places when dragged past each other.
**`thumbSize`**
Type: `{ width: number; height: number }`
Required: false
Default Value: `undefined`
Description: The slider thumbs dimensions
**`value`**
Type: `number[]`
Required: false
Default Value: `undefined`
Description: The controlled value of the slider
#### Data Attributes
**`data-scope`**: slider
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the slider
**`data-dragging`**: Present when in the dragging state
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
### 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`**: slider
**`data-part`**: control
**`data-dragging`**: Present when in the dragging state
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the control
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
### DraggingIndicator
#### 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`**: slider
**`data-part`**: dragging-indicator
**`data-orientation`**: The orientation of the draggingindicator
**`data-state`**: "open" | "closed"
### 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`**: slider
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the label
**`data-invalid`**: Present when invalid
**`data-dragging`**: Present when in the dragging state
**`data-focus`**: Present when focused
### 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.
#### Data Attributes
**`data-scope`**: slider
**`data-part`**: marker-group
**`data-orientation`**: The orientation of the markergroup
### Marker
#### Props
**`value`**
Type: `number`
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`**: slider
**`data-part`**: marker
**`data-orientation`**: The orientation of the marker
**`data-value`**: The value of the item
**`data-disabled`**: Present when disabled
**`data-state`**:
### Range
#### 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`**: slider
**`data-part`**: range
**`data-dragging`**: Present when in the dragging state
**`data-focus`**: Present when focused
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the range
### RootProvider
#### Props
**`value`**
Type: `UseSliderReturn`
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
**`index`**
Type: `number`
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.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: slider
**`data-part`**: thumb
**`data-index`**: The index of the item
**`data-name`**:
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the thumb
**`data-focus`**: Present when focused
**`data-dragging`**: Present when in the dragging state
### Track
#### 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`**: slider
**`data-part`**: track
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-dragging`**: Present when in the dragging state
**`data-orientation`**: The orientation of the track
**`data-focus`**: Present when focused
### 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.
#### Data Attributes
**`data-scope`**: slider
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the valuetext
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `value` | `number[]` | The value of the slider. |
| `dragging` | `boolean` | Whether the slider is being dragged. |
| `focused` | `boolean` | Whether the slider is focused. |
| `setValue` | `(value: number[]) => void` | Function to set the value of the slider. |
| `getThumbValue` | `(index: number) => number` | Returns the value of the thumb at the given index. |
| `setThumbValue` | `(index: number, value: number) => void` | Sets the value of the thumb at the given index. |
| `getValuePercent` | `(value: number) => number` | Returns the percent of the thumb at the given index. |
| `getPercentValue` | `(percent: number) => number` | Returns the value of the thumb at the given percent. |
| `getThumbPercent` | `(index: number) => number` | Returns the percent of the thumb at the given index. |
| `setThumbPercent` | `(index: number, percent: number) => void` | Sets the percent of the thumb at the given index. |
| `getThumbMin` | `(index: number) => number` | Returns the min value of the thumb at the given index. |
| `getThumbMax` | `(index: number) => number` | Returns the max value of the thumb at the given index. |
| `increment` | `(index: number) => void` | Function to increment the value of the slider at the given index. |
| `decrement` | `(index: number) => void` | Function to decrement the value of the slider at the given index. |
| `focus` | `VoidFunction` | Function to focus the slider. This focuses the first thumb. |
## Accessibility
Complies with the [Slider WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).
### Keyboard Support
**`ArrowRight`**
Description: Increments the slider based on defined step
**`ArrowLeft`**
Description: Decrements the slider based on defined step
**`ArrowUp`**
Description: Increases the value by the step amount.
**`ArrowDown`**
Description: Decreases the value by the step amount.
**`PageUp`**
Description: Increases the value by the largeStep amount.
**`PageDown`**
Description: Decreases the value by the largeStep amount.
**`Shift + ArrowUp`**
Description: Increases the value by the largeStep amount.
**`Shift + ArrowDown`**
Description: Decreases the value by the largeStep amount.
**`Home`**
Description: Sets the value to its minimum.
**`End`**
Description: Sets the value to its maximum.
# Splitter
## Anatomy
```tsx
```
## Examples
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const Basic = () => (
A
B
)
```
### Context
Access the splitter's API with `Splitter.Context` or the `useSplitterContext` hook. This lets you resize panels
programmatically:
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import button from 'styles/button.module.css'
import styles from 'styles/splitter.module.css'
export const Context = () => (
{(splitter) => (
<>
splitter().resizePanel('a', 10)}>
Set to 10%
splitter().resizePanel('b', 10)}>
Set to 10%
>
)}
)
```
### Vertical
By default, the Splitter component is horizontal. If you need a vertical splitter, use the `orientation` prop:
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const Vertical = () => (
A
B
)
```
### Collapsible Panels
To make a panel collapsible, set the `collapsible` prop to `true` on the panel you want to make collapsible.
Additionally, you can use the `collapsedSize` prop to set the size of the panel when it's collapsed.
> This can be useful for building sidebar layouts.
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const Collapsible = () => (
A
B
)
```
### Multiple Panels
Here's an example of how to use the `Splitter` component with multiple panels.
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const MultiplePanels = () => (
A
B
C
)
```
### Root Provider
An alternative way to control the splitter is to use the `RootProvider` component and the `useSplitter` hook. This way
you can access the state and methods from outside the component.
```tsx
import { Splitter, useSplitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const RootProvider = () => {
const splitter = useSplitter({
defaultSize: [50, 50],
panels: [{ id: 'a' }, { id: 'b' }],
})
return (
current size: {JSON.stringify(splitter().getSizes())}
A
B
)
}
```
### Resize Indicator
Use the `Splitter.ResizeTriggerIndicator` component to show a visual indicator on the resize handle.
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
export const ResizeIndicator = () => (
A
B
)
```
### Dynamic Collapsible
Use the `collapsePanel()` and `expandPanel()` methods to programmatically control panel collapse based on viewport size.
This is useful for responsive sidebar layouts that collapse on smaller screens.
```tsx
import { Splitter, useSplitter } from '@ark-ui/solid/splitter'
import { createEffect, createSignal, onMount } from 'solid-js'
import styles from 'styles/splitter.module.css'
export const DynamicCollapsible = () => {
const [rootSize, setRootSize] = createSignal(null)
let ref: HTMLDivElement | undefined
onMount(() => {
const handleResize = () => setRootSize(ref?.offsetWidth ?? null)
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
})
const isBelowMd = () => rootSize() != null && rootSize()! < 600
const splitter = useSplitter(() => ({
panels: [{ id: 'a', collapsible: isBelowMd(), collapsedSize: 5, minSize: 20, maxSize: 40 }, { id: 'b' }],
defaultSize: [15, 85],
}))
createEffect(() => {
if (isBelowMd()) splitter().collapsePanel('a')
else splitter().expandPanel('a')
})
return (
(ref = el)}>
A
B
)
}
```
### Nested
Nest splitters to build grid-like layouts. Use `Splitter.createRegistry()` to create a shared registry between splitter
instances — this enables multi-drag at intersection points where horizontal and vertical splitters meet.
```tsx
import { Splitter } from '@ark-ui/solid/splitter'
import styles from 'styles/splitter.module.css'
const registry = Splitter.createRegistry()
export const Nested = () => (
Left
Top
Bottom
Right
)
```
## API Reference
### Props
### Root
#### Props
**`panels`**
Type: `PanelData[]`
Required: true
Default Value: `undefined`
Description: The size constraints of the panels.
**`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.
**`defaultSize`**
Type: `PanelSize[]`
Required: false
Default Value: `undefined`
Description: The initial size of the panels when rendered.
Use when you don't need to control the size of the panels.
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
root: string
resizeTrigger: (id: string) => string
label: (id: string) => string
panel: (id: string | number) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the splitter. Useful for composition.
**`keyboardResizeBy`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The number of pixels to resize the panel by when the keyboard is used.
**`nonce`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The nonce for the injected splitter cursor stylesheet.
**`onCollapse`**
Type: `(details: ExpandCollapseDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when a panel is collapsed.
**`onExpand`**
Type: `(details: ExpandCollapseDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when a panel is expanded.
**`onResize`**
Type: `(details: ResizeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter is resized.
**`onResizeEnd`**
Type: `(details: ResizeEndDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter resize ends.
**`onResizeStart`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter resize starts.
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the splitter. Can be `horizontal` or `vertical`
**`registry`**
Type: `SplitterRegistry`
Required: false
Default Value: `undefined`
Description: The splitter registry to use for multi-drag support.
When provided, enables dragging at the intersection of multiple splitters.
**`size`**
Type: `PanelSize[]`
Required: false
Default Value: `undefined`
Description: The controlled size data of the panels
#### Data Attributes
**`data-scope`**: splitter
**`data-part`**: root
**`data-orientation`**: The orientation of the splitter
**`data-dragging`**: Present when in the dragging state
### Panel
#### Props
**`id`**
Type: `string`
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`**: splitter
**`data-part`**: panel
**`data-orientation`**: The orientation of the panel
**`data-dragging`**: Present when in the dragging state
**`data-id`**:
**`data-index`**: The index of the item
### Registry
#### Props
**`hitAreaMargins`**
Type: `HitAreaMargins`
Required: false
Default Value: `undefined`
Description: The hit area margins for resize handles.
Larger margins make it easier to grab handles, especially on touch devices.
**`nonce`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The nonce for the injected cursor stylesheet (for CSP compliance).
### ResizeTriggerIndicator
#### 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.
### ResizeTrigger
#### Props
**`id`**
Type: `ResizeTriggerId`
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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: splitter
**`data-part`**: resize-trigger
**`data-id`**:
**`data-orientation`**: The orientation of the resizetrigger
**`data-focus`**: Present when focused
**`data-dragging`**: Present when in the dragging state
**`data-disabled`**: Present when disabled
### RootProvider
#### Props
**`value`**
Type: `UseSplitterReturn`
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 splitter is currently being resized. |
| `orientation` | `"horizontal" | "vertical"` | The orientation of the splitter. |
| `getSizes` | `() => number[]` | Returns the current sizes of the panels. |
| `setSizes` | `(size: PanelSize[]) => void` | Sets the sizes of the panels. |
| `getItems` | `() => SplitterItem[]` | Returns the items of the splitter. |
| `getPanels` | `() => PanelData[]` | Returns the panels of the splitter. |
| `getPanelById` | `(id: PanelId) => PanelData` | Returns the panel with the specified id. |
| `getPanelSize` | `(id: PanelId) => number` | Returns the size of the specified panel. |
| `isPanelCollapsed` | `(id: PanelId) => boolean` | Returns whether the specified panel is collapsed. |
| `isPanelExpanded` | `(id: PanelId) => boolean` | Returns whether the specified panel is expanded. |
| `collapsePanel` | `(id: PanelId) => void` | Collapses the specified panel. |
| `expandPanel` | `(id: PanelId, minSize?: number) => void` | Expands the specified panel. |
| `resizePanel` | `(id: PanelId, unsafePanelSize: number) => void` | Resizes the specified panel. |
| `getLayout` | `() => string` | Returns the layout of the splitter. |
| `resetSizes` | `VoidFunction` | Resets the splitter to its initial state. |
| `getResizeTriggerState` | `(props: ResizeTriggerProps) => ResizeTriggerState` | Returns the state of the resize trigger. |
## Accessibility
Complies with the [Window Splitter WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/).
# Steps
## Anatomy
```tsx
```
## Examples
### Basic
Here's a basic example of the `Steps` component.
```tsx
import { Steps } from '@ark-ui/solid/steps'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/steps.module.css'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Basic = () => {
return (
{(item, index) => (
{index() + 1}
{item.title}
)}
{(item, index) => (
{item.title} - {item.description}
)}
Steps Complete - Thank you for filling out the form!
Back
Next
)
}
```
### Controlled
Using the `RootProvider` component, you can control the active step by using the `step` prop and handling the
`onStepChange` event.
```tsx
import { Steps } from '@ark-ui/solid/steps'
import { For, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/steps.module.css'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Controlled = () => {
const [step, setStep] = createSignal(0)
return (
current step: {step() + 1}
setStep(details.step)}
>
{(item, index) => (
{index() + 1}
{item.title}
)}
{(item, index) => (
{item.title} - {item.description}
)}
Steps Complete - Thank you for filling out the form!
Back
Next
)
}
```
### Root Provider
An alternative way to control the steps is to use the `RootProvider` component and the `useSteps` hook. This way you can
access the state and methods from outside the component.
```tsx
import { Steps, useSteps } from '@ark-ui/solid/steps'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/steps.module.css'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const RootProvider = () => {
const steps = useSteps({ count: items.length })
return (
current step: {steps().value + 1}
{(item, index) => (
{index() + 1}
{item.title}
)}
{(item, index) => (
{item.title} - {item.description}
)}
Steps Complete - Thank you for filling out the form!
Back
Next
)
}
```
### Vertical
Use the `orientation` prop to display the steps vertically.
```tsx
import { Steps } from '@ark-ui/solid/steps'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/steps.module.css'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Vertical = () => {
return (
{(item, index) => (
{index() + 1}
{item.title}
)}
{(item, index) => (
{item.title} - {item.description}
Back
Next
)}
Steps Complete - Thank you for filling out the form!
Back
)
}
```
## 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.
**`count`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The total number of steps
**`defaultStep`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The initial value of the stepper when rendered.
Use when you don't need to control the value of the stepper.
**`ids`**
Type: `ElementIds`
Required: false
Default Value: `undefined`
Description: The custom ids for the stepper elements
**`isStepSkippable`**
Type: `(index: number) => boolean`
Required: false
Default Value: `() => false`
Description: Whether a step can be skipped during navigation.
Skippable steps are bypassed when using next/prev.
**`isStepValid`**
Type: `(index: number) => boolean`
Required: false
Default Value: `() => true`
Description: Whether a step is valid. Invalid steps block forward navigation in linear mode.
**`linear`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the stepper requires the user to complete the steps in order
**`onStepChange`**
Type: `(details: StepChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback to be called when the value changes
**`onStepComplete`**
Type: `VoidFunction`
Required: false
Default Value: `undefined`
Description: Callback to be called when a step is completed
**`onStepInvalid`**
Type: `(details: StepInvalidDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when navigation is blocked due to an invalid step.
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the stepper
**`step`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The controlled value of the stepper
#### Data Attributes
**`data-scope`**: steps
**`data-part`**: root
**`data-orientation`**: The orientation of the steps
### CompletedContent
#### 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.
### Content
#### Props
**`index`**
Type: `number`
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`**: steps
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-orientation`**: The orientation of the content
### 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`**: steps
**`data-part`**: indicator
**`data-complete`**: Present when the indicator value is complete
**`data-current`**: Present when current
**`data-incomplete`**:
### Item
#### Props
**`index`**
Type: `number`
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`**: steps
**`data-part`**: item
**`data-orientation`**: The orientation of the item
**`data-skippable`**:
### 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`**: steps
**`data-part`**: list
**`data-orientation`**: The orientation of the list
### 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.
### 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.
### Progress
#### 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`**: steps
**`data-part`**: progress
**`data-complete`**: Present when the progress value is complete
### RootProvider
#### Props
**`value`**
Type: `UseStepsReturn`
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.
### Separator
#### 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`**: steps
**`data-part`**: separator
**`data-orientation`**: The orientation of the separator
**`data-complete`**: Present when the separator value is complete
**`data-current`**: Present when current
**`data-incomplete`**:
### 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`**: steps
**`data-part`**: trigger
**`data-state`**: "open" | "closed"
**`data-orientation`**: The orientation of the trigger
**`data-complete`**: Present when the trigger value is complete
**`data-current`**: Present when current
**`data-incomplete`**:
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `value` | `number` | The value of the stepper. |
| `percent` | `number` | The percentage of the stepper. |
| `count` | `number` | The total number of steps. |
| `hasNextStep` | `boolean` | Whether the stepper has a next step. |
| `hasPrevStep` | `boolean` | Whether the stepper has a previous step. |
| `isCompleted` | `boolean` | Whether the stepper is completed. |
| `isStepValid` | `(index: number) => boolean` | Check if a specific step is valid (lazy evaluation) |
| `isStepSkippable` | `(index: number) => boolean` | Check if a specific step can be skipped |
| `setStep` | `(step: number) => void` | Function to set the value of the stepper. |
| `goToNextStep` | `VoidFunction` | Function to go to the next step. |
| `goToPrevStep` | `VoidFunction` | Function to go to the previous step. |
| `resetStep` | `VoidFunction` | Function to go to reset the stepper. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of the item at the given index. |
# Switch
## Anatomy
```tsx
```
## Examples
```tsx
import { Switch } from '@ark-ui/solid/switch'
import styles from 'styles/switch.module.css'
export const Basic = () => (
Label
)
```
### Controlled
For a controlled Switch component, the state of the toggle is managed using the checked prop, and updates when the
`onCheckedChange` event handler is called:
```tsx
import { Switch } from '@ark-ui/solid/switch'
import { createSignal } from 'solid-js'
import styles from 'styles/switch.module.css'
export const Controlled = () => {
const [checked, setChecked] = createSignal(false)
return (
setChecked(e.checked)}>
Label
)
}
```
### Root Provider
An alternative way to control the switch is to use the `RootProvider` component and the `useSwitch` hook. This way you
can access the state and methods from outside the component.
```tsx
import { Switch, useSwitch } from '@ark-ui/solid/switch'
import button from 'styles/button.module.css'
import styles from 'styles/switch.module.css'
export const RootProvider = () => {
const _switch = useSwitch()
return (
_switch().toggleChecked()}>
Toggle
Label
)
}
```
### Field
The `Field` component helps manage form-related state and accessibility attributes of a switch. It includes handling
ARIA labels, helper text, and error text to ensure proper accessibility.
```tsx
import { Field } from '@ark-ui/solid/field'
import { Switch } from '@ark-ui/solid/switch'
import field from 'styles/field.module.css'
import styles from 'styles/switch.module.css'
export const WithField = () => (
Label
Additional Info
Error Info
)
```
### Context
Access the switch's state with `Switch.Context` or the `useSwitchContext` hook. This lets you customize the component
based on its current state:
```tsx
import { Switch } from '@ark-ui/solid/switch'
import styles from 'styles/switch.module.css'
export const Context = () => (
{(context) => (
Feature is {context().checked ? 'enabled' : 'disabled'}
)}
)
```
## Guides
### asChild
The `Switch.Root` element of the switch is a `label` element. This is because the switch 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 `Switch.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: `boolean`
Required: false
Default Value: `undefined`
Description: The controlled checked state of the switch
**`defaultChecked`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The initial checked state of the switch when rendered.
Use when you don't need to control the checked state of the switch.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the switch is disabled.
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The id of the form that the switch belongs to
**`ids`**
Type: `Partial<{ root: string; hiddenInput: string; control: string; label: string; thumb: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the switch. Useful for composition.
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the switch is marked as invalid.
**`label`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name of the input field in a switch
(Useful for form submission).
**`onCheckedChange`**
Type: `(details: CheckedChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to call when the switch is clicked.
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the switch is read-only
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: If `true`, the switch input is marked as required,
**`value`**
Type: `string | number`
Required: false
Default Value: `"on"`
Description: The value of switch 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`**: "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### Control
#### 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`**: "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### 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<'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`**: "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseSwitchReturn`
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.
### Thumb
#### 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`**: "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `checked` | `boolean` | Whether the switch is checked |
| `disabled` | `boolean | undefined` | Whether the switch is disabled |
| `focused` | `boolean | undefined` | Whether the switch is focused |
| `setChecked` | `(checked: boolean) => void` | Sets the checked state of the switch. |
| `toggleChecked` | `VoidFunction` | Toggles the checked state of the switch. |
## Accessibility
Complies with the [Switch WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/).
### Keyboard Support
**`Space + Enter`**
Description: Toggle the switch
# Tabs
## Anatomy
```tsx
```
## Examples
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const Basic = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Controlled
To create a controlled Tabs component, manage the current selected tab using the `value` prop and update it when the
`onValueChange` event handler is called:
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import { createSignal } from 'solid-js'
import styles from 'styles/tabs.module.css'
export const Controlled = () => {
const [value, setValue] = createSignal('account')
return (
setValue(e.value)}>
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
}
```
### Root Provider
An alternative way to control the tabs is to use the `RootProvider` component and the `useTabs` hook. This way you can
access the state and methods from outside the component.
```tsx
import { Tabs, useTabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const RootProvider = () => {
const tabs = useTabs({ defaultValue: 'account' })
return (
selected: {tabs().value}
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
}
```
### Indicator
To provide a visual cue for the selected tab, use the `Tabs.Indicator` component:
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const Indicator = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Disabled
To disable a tab, simply pass the `disabled` prop to the `Tabs.Trigger` component:
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const DisabledTab = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Vertical
The default orientation of the tabs is `horizontal`. To change the orientation, set the `orientation` prop to
`vertical`.
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const Vertical = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Lazy Mount
Lazy mounting is a feature that allows the content of a tab to be rendered only when the tab is first activated. This is
useful for performance optimization, especially when tab content is large or complex. To enable lazy mounting, use the
`lazyMount` prop on the `Tabs.Content` component.
In addition, the `unmountOnExit` prop can be used in conjunction with `lazyMount` to unmount the tab content when the
tab is deactivated, freeing up resources. The next time the tab is activated, its content will be re-rendered.
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const LazyMount = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Manual Activation
By default, the tab can be selected when it receives focus from either the keyboard or pointer interaction. This is
called automatic tab activation.
In contrast, manual tab activation means the tab is selected with the
Enter key or by clicking on the tab.
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const ManualActivation = () => (
Account
Password
Billing
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
### Links
Use the `asChild` prop to render tab triggers as anchor links. This is useful for SEO and allows tabs to work with
browser navigation.
```tsx
import { Tabs } from '@ark-ui/solid/tabs'
import styles from 'styles/tabs.module.css'
export const Links = () => (
(
Account
)}
/>
(
Password
)}
/>
(
Billing
)}
/>
Make changes to your account here.
Change your password here.
Manage your billing and payment details.
)
```
## Guides
### Router Integration
When using frameworks like Next.js, Remix, or React Router, controlling the active tabs based on the URL can be useful.
To achieve this, you need to do two things:
- Set the `value` prop to the current URL path.
- Listen to the `onValueChange` event and update the URL path.
Here's an example using Remix Router
```tsx
import { Tabs } from '@ark-ui//tabs'
import { useLocation, useNavigate, Link } from '@remix-run/react'
export default function App() {
const { pathname } = useLocation()
const navigate = useNavigate()
const lastPathFragment = pathname.substring(pathname.lastIndexOf('/') + 1)
const activeTab = lastPathFragment.length > 0 ? lastPathFragment : 'homepage'
return (
{
navigate(`/${value === 'home' ? '' : value}`)
}}
>
Home
Page 1
Page 2
)
}
```
## API Reference
### Props
### Root
#### Props
**`activationMode`**
Type: `'manual' | 'automatic'`
Required: false
Default Value: `"automatic"`
Description: The activation mode of the tabs. Can be `manual` or `automatic`
- `manual`: Tabs are activated when clicked or press `enter` key.
- `automatic`: Tabs are activated when receiving focus
**`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.
**`composite`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tab is composite
**`defaultValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial selected tab value when rendered.
Use when you don't need to control the selected tab value.
**`deselectable`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the active tab can be deselected when clicking on it.
**`ids`**
Type: `Partial<{
root: string
trigger: (value: string) => string
list: string
content: (value: string) => string
indicator: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the tabs. Useful for composition.
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`loopFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the keyboard navigation will loop from last tab to first, and vice versa.
**`navigate`**
Type: `(details: NavigateDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to navigate to the selected tab when clicking on it.
Useful if tab triggers are anchor elements.
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback to be called when the focused tab changes
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback to be called when the selected/active tab changes
**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the tabs. Can be `horizontal` or `vertical`
- `horizontal`: only left and right arrow key navigation will work.
- `vertical`: only up and down arrow key navigation will work.
**`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 selected tab value
#### Data Attributes
**`data-scope`**: tabs
**`data-part`**: root
**`data-orientation`**: The orientation of the tabs
**`data-focus`**: Present when focused
### TabContent
#### Props
**`value`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The value of the tab
**`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.
### TabIndicator
#### 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.
### TabList
#### 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.
### TabTrigger
#### Props
**`value`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The value of the tab
**`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 tab is disabled
### RootProvider
#### Props
**`value`**
Type: `UseTabsReturn`
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 |
|----------|------|-------------|
| `value` | `string | null` | The current value of the tabs. |
| `focusedValue` | `string | null` | The value of the tab that is currently focused. |
| `setValue` | `(value: string) => void` | Sets the value of the tabs. |
| `clearValue` | `VoidFunction` | Clears the value of the tabs. |
| `setIndicatorRect` | `(value: string) => void` | Sets the indicator rect to the tab with the given value |
| `syncTabIndex` | `VoidFunction` | Synchronizes the tab index of the content element.
Useful when rendering tabs within a select or combobox |
| `focus` | `VoidFunction` | Set focus on the selected tab trigger |
| `selectNext` | `(fromValue?: string) => void` | Selects the next tab |
| `selectPrev` | `(fromValue?: string) => void` | Selects the previous tab |
| `getTriggerState` | `(props: TriggerProps) => TriggerState` | Returns the state of the trigger with the given props |
## Accessibility
Complies with the [Tabs WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/).
### Keyboard Support
**`Tab`**
Description: When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content.
**`ArrowDown`**
Description: Moves focus to the next trigger in vertical orientation and activates its associated content.
**`ArrowRight`**
Description: Moves focus to the next trigger in horizontal orientation and activates its associated content.
**`ArrowUp`**
Description: Moves focus to the previous trigger in vertical orientation and activates its associated content.
**`ArrowLeft`**
Description: Moves focus to the previous trigger in horizontal orientation and activates its associated content.
**`Home`**
Description: Moves focus to the first trigger and activates its associated content.
**`End`**
Description: Moves focus to the last trigger and activates its associated content.
**`Enter + Space`**
Description: In manual mode, when a trigger is focused, moves focus to its associated content.
# Tags Input
## Anatomy
```tsx
```
## Examples
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Basic = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` props to programmatically control the tags input's state. This allows you to manage
the tags array externally and respond to changes.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index, createSignal } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Controlled = () => {
const [value, setValue] = createSignal(['vue', 'react'])
return (
setValue(details.value)} class={styles.Root}>
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Controlled Input Value
Use the `inputValue` and `onInputValueChange` props to control the text input field independently. This is useful for
clearing the input or pre-filling it programmatically.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index, createSignal } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const ControlledInputValue = () => {
const [inputValue, setInputValue] = createSignal('')
return (
setInputValue(details.inputValue)}
class={styles.Root}
>
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Root Provider
An alternative way to control the tags input is to use the `RootProvider` component and the `useTagsInput` hook. This
way you can access the state and methods from outside the component.
```tsx
import { TagsInput, useTagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tags-input.module.css'
export const RootProvider = () => {
const tagsInput = useTagsInput()
return (
tagsInput().focus()}>
Focus
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Field
The `Field` component helps manage form-related state and accessibility attributes of a tags input. It includes handling
ARIA labels, helper text, and error text to ensure proper accessibility.
```tsx
import { Field } from '@ark-ui/solid/field'
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import field from 'styles/field.module.css'
import styles from 'styles/tags-input.module.css'
export const WithField = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
Additional Info
Error Info
)
}
```
### Max Tags
To limit the number of tags within the component, you can set the `max` property to the limit you want. The default
value is `Infinity`.
When the tag reaches the limit, new tags cannot be added except the `allowOverflow` prop is set to `true`.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const MaxWithOverflow = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Custom Delimiter
Use the `delimiter` prop with a regex pattern to specify multiple characters that can separate tags. By default, only
the Enter key creates tags.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Delimiter = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Disabled
Use the `disabled` prop to make the tags input non-interactive. Users won't be able to add, remove, or edit tags.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Disabled = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Invalid
Use the `invalid` prop to mark the tags input as invalid for form validation purposes.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Invalid = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Max Length
Use the `maxLength` prop to limit the number of characters allowed per tag. This prevents users from creating overly
long tags.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const MaxTagLength = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Read-only
Use the `readOnly` prop to make tags visible but not editable. Users can view tags but cannot add, remove, or modify
them.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Readonly = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Validation
Before a tag is added, the `validate` function is called to determine whether to accept or reject a tag.
A common use-case for validating tags is preventing duplicates or validating the data type.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const Validation = () => {
return (
{
return !details.value.includes(details.inputValue)
}}
class={styles.Root}
>
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Blur behavior
When the tags input is blurred, you can configure the action the component should take by passing the `blurBehavior`
prop.
- `add` — Adds the tag to the list of tags.
- `clear` — Clears the tags input value.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const BlurBehavior = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Paste behavior
To add a tag when a arbitrary value is pasted in the input element, pass the `addOnPaste` prop.
When a value is pasted, the component will:
- check if the value is a valid tag based on the `validate` option
- split the value by the `delimiter` option passed
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const PasteBehavior = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Disable Editing
by default the tags can be edited by double-clicking on the tag or focusing on them and pressing
Enter . To disable this behavior, pass `editable={false}`
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const DisabledEditing = () => {
return (
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Programmatic Control
Use the `useTagsInput` hook with `RootProvider` to access the component's API methods like `addValue()`, `setValue()`,
and `clearValue()` for full programmatic control.
```tsx
import { TagsInput, useTagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tags-input.module.css'
export const ProgrammaticControl = () => {
const tagsInput = useTagsInput()
return (
tagsInput().addValue('React')}>
Add React
tagsInput().addValue('Solid')}>
Add Solid
tagsInput().setValue(['Vue', 'Svelte'])}>
Set to Vue & Svelte
tagsInput().clearValue()}>
Clear All
{(api) => (
<>
Frameworks
{(value, index) => (
{value()}
)}
>
)}
)
}
```
### Sanitize Value
Use the `sanitizeValue` prop to normalize tag values before they're added. This runs on every new tag — useful for
trimming whitespace, converting to lowercase, or any other formatting you need.
```tsx
import { TagsInput } from '@ark-ui/solid/tags-input'
import { XIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/tags-input.module.css'
export const SanitizeValue = () => (
value.trim().toLowerCase()}>
{(api) => (
<>
Email Addresses
{(value, index) => (
{value()}
)}
>
)}
)
```
### Combobox
Combine TagsInput with Combobox to create an autocomplete tags input. This pattern uses shared IDs between both
components and the `asChild` prop to compose the inputs together.
```tsx
import { Combobox, useCombobox, useListCollection } from '@ark-ui/solid/combobox'
import { useFilter } from '@ark-ui/solid/locale'
import { TagsInput, useTagsInput } from '@ark-ui/solid/tags-input'
import { CheckIcon, XIcon } from 'lucide-solid'
import { For, createUniqueId } from 'solid-js'
import { Portal } from 'solid-js/web'
import combobox from 'styles/combobox.module.css'
import styles from 'styles/tags-input.module.css'
export const WithCombobox = () => {
const filterFn = useFilter({ sensitivity: 'base' })
const { collection, filter } = useListCollection({
initialItems: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Preact', 'Next.js', 'Astro', 'Nuxt'],
filter: filterFn().contains,
})
const uid = createUniqueId()
const tagsInput = useTagsInput({
ids: { input: `input_${uid}`, control: `control_${uid}` },
})
const comboboxApi = useCombobox({
ids: { input: `input_${uid}`, control: `control_${uid}` },
collection: collection(),
onInputValueChange(details) {
filter(details.inputValue)
},
value: [],
allowCustomValue: true,
onValueChange: (details) => {
if (details.value[0]) {
tagsInput().addValue(details.value[0])
}
},
selectionBehavior: 'clear',
})
return (
Frameworks
{(value, index) => (
{value}
)}
}
/>
No frameworks found
{(item) => (
{item}
)}
)
}
```
## Guides
### Navigation
When the input has an empty value or the caret is at the start position, the tags can be selected by using the arrow
left and arrow right keys. When "visual" focus in on any tag:
- Pressing Enter or double-clicking on the tag will put it in edit mode, allowing the user change its value
and press Enter to commit the changes.
- Pressing Delete or Backspace will delete the tag that has _visual_ focus.
## API Reference
### Props
### Root
#### Props
**`addOnPaste`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to add a tag when you paste values into the tag input
**`allowDuplicates`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to allow duplicate tags.
**`allowOverflow`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow tags to exceed max. In this case,
we'll attach `data-invalid` to the root
**`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 the input should be auto-focused
**`blurBehavior`**
Type: `'clear' | 'add'`
Required: false
Default Value: `undefined`
Description: The behavior of the tags input when the input is blurred
- `"add"`: add the input value as a new tag
- `"clear"`: clear the input value
**`defaultInputValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial tag input value when rendered.
Use when you don't need to control the tag input value.
**`defaultValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial tag value when rendered.
Use when you don't need to control the tag value.
**`delimiter`**
Type: `string | RegExp`
Required: false
Default Value: `","`
Description: The character that serves has:
- event key to trigger the addition of a new tag
- character used to split tags when pasting into the input
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tags input should be disabled
**`editable`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether a tag can be edited after creation, by pressing `Enter` or double clicking.
**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying input element.
**`ids`**
Type: `Partial<{
root: string
input: string
hiddenInput: string
clearBtn: string
label: string
control: string
item: (opts: ItemProps) => string
itemDeleteTrigger: (opts: ItemProps) => string
itemInput: (opts: ItemProps) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the tags input. Useful for composition.
**`inputValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled tag input's value
**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tags input is invalid
**`max`**
Type: `number`
Required: false
Default Value: `Infinity`
Description: The max number of tags
**`maxLength`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The max length of the input.
**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The name attribute for the input. Useful for form submissions
**`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: Callback fired when a tag is highlighted by pointer or keyboard navigation
**`onInputValueChange`**
Type: `(details: InputValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback fired when the input value is updated
**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when an interaction happens outside the component
**`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: Callback fired when the tag values is updated
**`onValueInvalid`**
Type: `(details: ValidityChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback fired when the max tag count is reached or the `validateTag` function returns `false`
**`placeholder`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The placeholder text for the input
**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tags input should be read-only
**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tags input is required
**`sanitizeValue`**
Type: `(value: string) => string`
Required: false
Default Value: `(value) => value.trim()`
Description: Function to sanitize the tag value before adding.
Useful for trimming whitespace, normalizing case, or stripping special characters.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`validate`**
Type: `(details: ValidateArgs) => boolean`
Required: false
Default Value: `undefined`
Description: Returns a boolean that determines whether a tag can be added.
Useful for preventing duplicates or invalid tag values.
**`value`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled tag value
#### Data Attributes
**`data-scope`**: tags-input
**`data-part`**: root
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`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.
#### Data Attributes
**`data-scope`**: tags-input
**`data-part`**: clear-trigger
**`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`**: tags-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.
### 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`**: tags-input
**`data-part`**: input
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-empty`**: Present when the content is empty
### ItemDeleteTrigger
#### 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`**: tags-input
**`data-part`**: item-delete-trigger
**`data-disabled`**: Present when disabled
**`data-highlighted`**: Present when highlighted
### ItemInput
#### 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.
### ItemPreview
#### 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`**: tags-input
**`data-part`**: item-preview
**`data-value`**: The value of the item
**`data-disabled`**: Present when disabled
**`data-highlighted`**: Present when highlighted
### Item
#### Props
**`index`**
Type: `string | number`
Required: true
Default Value: `undefined`
Description: undefined
**`value`**
Type: `string`
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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: tags-input
**`data-part`**: item
**`data-value`**: The value of the item
**`data-disabled`**: Present when disabled
### 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`**: tags-input
**`data-part`**: item-text
**`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`**: tags-input
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`data-required`**: Present when required
### RootProvider
#### Props
**`value`**
Type: `UseTagsInputReturn`
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 |
|----------|------|-------------|
| `empty` | `boolean` | Whether the tags are empty |
| `inputValue` | `string` | The value of the tags entry input. |
| `value` | `string[]` | The value of the tags as an array of strings. |
| `valueAsString` | `string` | The value of the tags as a string. |
| `count` | `number` | The number of the tags. |
| `atMax` | `boolean` | Whether the tags have reached the max limit. |
| `setValue` | `(value: string[]) => void` | Function to set the value of the tags. |
| `clearValue` | `(id?: string) => void` | Function to clear the value of the tags. |
| `addValue` | `(value: string) => void` | Function to add a tag to the tags. |
| `setValueAtIndex` | `(index: number, value: string) => void` | Function to set the value of a tag at the given index. |
| `setInputValue` | `(value: string) => void` | Function to set the value of the tags entry input. |
| `clearInputValue` | `VoidFunction` | Function to clear the value of the tags entry input. |
| `focus` | `VoidFunction` | Function to focus the tags entry input. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a tag |
## Accessibility
### Keyboard Support
**`ArrowLeft`**
Description: Moves focus to the previous tag item
**`ArrowRight`**
Description: Moves focus to the next tag item
**`Backspace`**
Description: Deletes the tag item that has visual focus or the last tag item
**`Enter`**
Description: When a tag item has visual focus, it puts the tag in edit mode. When the input has focus, it adds the value to the list of tags
**`Delete`**
Description: Deletes the tag item that has visual focus
**`Control + V`**
Description: When `addOnPaste` is set. Adds the pasted value as a tags
# Timer
## Anatomy
```tsx
```
## Examples
```tsx
import { Timer } from '@ark-ui/solid/timer'
import { PauseIcon, PlayIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const Basic = () => (
days
:
hours
:
minutes
:
seconds
Play
Resume
Pause
)
```
### Countdown
You can create a countdown timer by setting the `countdown` prop to `true` and `startMs` to the initial time:
```tsx
import { Timer } from '@ark-ui/solid/timer'
import { PauseIcon, PlayIcon, RotateCcwIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const Countdown = () => (
minutes
:
seconds
Start
Pause
Reset
)
```
### Interval
Use the `interval` prop to control how frequently the timer updates. This is useful for displaying milliseconds:
```tsx
import { Timer } from '@ark-ui/solid/timer'
import { PauseIcon, PlayIcon, RotateCcwIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const Interval = () => (
seconds
.
ms
Start
Pause
Reset
)
```
### Events
The Timer component provides events that you can listen to for various timer-related actions.
- The `onComplete` event is triggered when the timer reaches its target time.
- The `onTick` event is called on each timer update, providing details about the current timer state.
```tsx
import { Timer } from '@ark-ui/solid/timer'
import { PlayIcon, RotateCcwIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const Events = () => {
const [ticks, setTicks] = createSignal(0)
return (
console.log('Timer completed')}
onTick={() => setTicks((t) => t + 1)}
>
minutes
:
seconds
Start
Reset
Ticks: {ticks()}
)
}
```
### Pomodoro
Here's an example of building a pomodoro timer that alternates between work and break sessions:
```tsx
import { Timer } from '@ark-ui/solid/timer'
import { PauseIcon, PlayIcon, RotateCcwIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const Pomodoro = () => {
const [isWorking, setIsWorking] = createSignal(true)
const [cycles, setCycles] = createSignal(0)
const handleComplete = () => {
setIsWorking(!isWorking())
if (!isWorking()) setCycles((c) => c + 1)
}
return (
{isWorking() ? 'Work Session' : 'Break Session'}
minutes
:
seconds
Start
Pause
Reset
Completed cycles: {cycles()}
)
}
```
### Root Provider
An alternative way to control the timer is to use the `RootProvider` component and the `useTimer` hook. This way you can
access the state and methods from outside the component.
```tsx
import { Timer, useTimer } from '@ark-ui/solid/timer'
import { PauseIcon, PlayIcon, RotateCcwIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/timer.module.css'
export const RootProvider = () => {
const timer = useTimer({ targetMs: 60 * 60 * 1000 })
return (
timer: {JSON.stringify(timer().time)}
hours
:
minutes
:
seconds
Start
Resume
Pause
Reset
)
}
```
## 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.
**`autoStart`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the timer should start automatically
**`countdown`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the timer should countdown, decrementing the timer on each tick.
**`ids`**
Type: `Partial<{ root: string; area: string }>`
Required: false
Default Value: `undefined`
Description: The ids of the timer parts
**`interval`**
Type: `number`
Required: false
Default Value: `1000`
Description: The interval in milliseconds to update the timer count.
**`onComplete`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the timer is completed
**`onTick`**
Type: `(details: TickDetails) => void`
Required: false
Default Value: `undefined`
Description: Function invoked when the timer ticks
**`startMs`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The total duration of the timer in milliseconds.
**`targetMs`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The minimum count of the timer in milliseconds.
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
### ActionTrigger
#### Props
**`action`**
Type: `TimerAction`
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.
### 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.
### 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.
### Item
#### Props
**`type`**
Type: `keyof Time`
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`**: timer
**`data-part`**: item
**`data-type`**: The type of the item
### RootProvider
#### Props
**`value`**
Type: `UseTimerReturn`
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.
### Separator
#### 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 |
|----------|------|-------------|
| `running` | `boolean` | Whether the timer is running. |
| `paused` | `boolean` | Whether the timer is paused. |
| `time` | `Time` | The formatted timer count value. |
| `formattedTime` | `Time` | The formatted time parts of the timer count. |
| `start` | `VoidFunction` | Function to start the timer. |
| `pause` | `VoidFunction` | Function to pause the timer. |
| `resume` | `VoidFunction` | Function to resume the timer. |
| `reset` | `VoidFunction` | Function to reset the timer. |
| `restart` | `VoidFunction` | Function to restart the timer. |
| `progressPercent` | `number` | The progress percentage of the timer. |
# Table of Contents
## Anatomy
```tsx
scrollContainer}>
```
## Examples
### Basic
Pass headings to `items`, and point `scrollEl` at the scrollable container so the TOC knows what to track.
```tsx
import { Toc } from '@ark-ui/solid/toc'
import styles from 'styles/toc.module.css'
const items = [
{ value: '01-introduction', depth: 2, label: 'Introduction', lines: 12 },
{ value: '01-getting-started', depth: 2, label: 'Getting Started', lines: 10 },
{ value: '01-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '01-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '01-conclusion', depth: 2, label: 'Conclusion', lines: 10 },
]
export const Basic = () => {
let contentRef: HTMLElement | null = null
return (
contentRef}>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{Array.from({ length: item.lines }).map(() => (
))}
))}
On this page
{items.map((item) => (
{item.label}
))}
)
}
```
### Nested Headings
Read `depth` in your own markup to indent sub-headings. Nothing is indented for you.
```tsx
import { Toc } from '@ark-ui/solid/toc'
import { Dynamic } from 'solid-js/web'
import styles from 'styles/toc.module.css'
const items = [
{ value: '02-introduction', depth: 2, label: 'Introduction', lines: 10 },
{ value: '02-getting-started', depth: 2, label: 'Getting Started', lines: 12 },
{ value: '02-installation', depth: 3, label: 'Installation', lines: 8 },
{ value: '02-configuration', depth: 3, label: 'Configuration', lines: 14 },
{ value: '02-api-reference', depth: 2, label: 'API Reference', lines: 10 },
{ value: '02-hooks', depth: 3, label: 'Hooks', lines: 8 },
{ value: '02-components', depth: 3, label: 'Components', lines: 12 },
{ value: '02-examples', depth: 2, label: 'Examples', lines: 10 },
]
export const Nested = () => {
let contentRef: HTMLElement | null = null
return (
contentRef}>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{[...Array(item.lines)].map(() => (
))}
))}
On this page
{items.map((item) => (
2 ? styles.ItemNested : styles.Item} item={item}>
{item.label}
))}
)
}
```
### Root Provider
Use `useToc` with `Toc.RootProvider` to reach `activeIds` from outside the tree, so other parts of your UI can follow
the reading position.
```tsx
import { Toc, useToc } from '@ark-ui/solid/toc'
import { Index } from 'solid-js'
import styles from 'styles/toc.module.css'
const items = [
{ value: '03-introduction', depth: 2, label: 'Introduction', lines: 12 },
{ value: '03-getting-started', depth: 2, label: 'Getting Started', lines: 10 },
{ value: '03-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '03-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '03-conclusion', depth: 2, label: 'Conclusion', lines: 10 },
]
export const RootProvider = () => {
let contentRef: HTMLElement | null = null
const toc = useToc({ items, rootMargin: '0px 0px -80% 0px', scrollEl: () => contentRef })
return (
(contentRef = el)}>
{(item) => (
)}
On this page
{(item) => (
{item().label}
)}
)
}
```
### With Collapsible
Wrap `Toc.Nav` in a `Collapsible` to let users hide the navigation. `Toc.Context` exposes `activeItems`, here driving a
progress ring.
```tsx
import { Collapsible } from '@ark-ui/solid/collapsible'
import { Toc } from '@ark-ui/solid/toc'
import { ChevronRightIcon } from 'lucide-solid'
import CollapsibleStyles from 'styles/Collapsible.module.css'
import styles from 'styles/toc.module.css'
const items = [
{ value: '04-introduction', depth: 2, label: 'Introduction', lines: 12 },
{ value: '04-getting-started', depth: 2, label: 'Getting Started', lines: 10 },
{ value: '04-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '04-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '04-conclusion', depth: 2, label: 'Conclusion', lines: 10 },
]
const RADIUS = 14
const CIRCUMFERENCE = 2 * Math.PI * RADIUS
export const WithCollapsible = () => {
let contentRef: HTMLElement | null = null
return (
contentRef}
>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{Array.from({ length: item.lines }).map(() => (
))}
))}
{(toc) => {
const activeIndex = () => {
const activeItems = toc().activeItems
return activeItems[0] ? items.findIndex((i) => i.value === activeItems[0].value) : -1
}
const activeLabel = () => (activeIndex() >= 0 ? items[activeIndex()].label : undefined)
const dashArray = () =>
`${(activeIndex() >= 0 ? (activeIndex() + 1) / items.length : 0) * CIRCUMFERENCE} ${CIRCUMFERENCE}`
return (
{activeIndex() >= 0 ? activeIndex() + 1 : '—'}
{activeLabel() ?? 'On this page'}
)
}}
{items.map((item, index) => (
{String(index + 1).padStart(2, '0')}
{item.label}
))}
)
}
```
### With Hover
Expand the navigation on `onMouseEnter` and collapse it on `onMouseLeave`, with a pin toggle to keep it open.
> **Note:** Hover does not exist on touch screens. Pair this with a pin button or a disclosure control so the navigation
> stays reachable on mobile.
```tsx
import { Toc } from '@ark-ui/solid/toc'
import { Swap } from '@ark-ui/solid/swap'
import { Pin, PinOff } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/toc.module.css'
const items = [
{ value: '05-introduction', depth: 2, label: 'Introduction', lines: 12 },
{ value: '05-getting-started', depth: 2, label: 'Getting Started', lines: 10 },
{ value: '05-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '05-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '05-conclusion', depth: 2, label: 'Conclusion', lines: 10 },
]
export const WithHover = () => {
const [pinned, setPinned] = createSignal(false)
const [hovered, setHovered] = createSignal(false)
let contentRef: HTMLElement | null = null
return (
contentRef}
>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{Array.from({ length: item.lines }).map(() => (
))}
))}
setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={() => {
if (!hovered() && !pinned()) setPinned(true)
}}
>
setPinned((v) => !v)}
aria-label={pinned() ? 'Unpin navigation' : 'Pin navigation'}
>
{items.map((item) => (
))}
{items.map((item) => (
{item.label}
))}
)
}
```
### With Indicator
Add `Toc.Indicator` inside `Toc.List` for a marker that slides to the active item.
```tsx
import { Toc } from '@ark-ui/solid/toc'
import styles from 'styles/toc.module.css'
const items = [
{ value: '06-introduction', depth: 2, label: 'Introduction', lines: 12 },
{ value: '06-getting-started', depth: 2, label: 'Getting Started', lines: 10 },
{ value: '06-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '06-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '06-conclusion', depth: 2, label: 'Conclusion', lines: 10 },
]
export const WithIndicator = () => {
let contentRef: HTMLElement | null = null
return (
contentRef}>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{Array.from({ length: item.lines }).map(() => (
))}
))}
On this page
{items.map((item) => (
{item.label}
))}
)
}
```
### With Rail
Give each item a small SVG offset by `depth`. Where neighbouring items sit at different depths, a bezier joins the two
positions so the rail steps rather than breaks.
```tsx
import { Toc } from '@ark-ui/solid/toc'
import { Show } from 'solid-js'
import styles from 'styles/toc.module.css'
const items = [
{ value: '07-overview', depth: 2, label: 'Overview', lines: 10 },
{ value: '07-installation', depth: 2, label: 'Installation', lines: 8 },
{ value: '07-package-manager', depth: 3, label: 'Package Manager', lines: 12 },
{ value: '07-peer-dependencies', depth: 3, label: 'Peer Dependencies', lines: 6 },
{ value: '07-usage', depth: 2, label: 'Usage', lines: 14 },
{ value: '07-server-components', depth: 3, label: 'Server Components', lines: 9 },
{ value: '07-styling', depth: 3, label: 'Styling', lines: 11 },
{ value: '07-theming', depth: 4, label: 'Theming', lines: 7 },
{ value: '07-api-reference', depth: 2, label: 'API Reference', lines: 12 },
]
// h2 sits at level 0; deeper headings step in, clamped so h5+ share h4's indent
const BASE = 8
const RAIL_STEP = 8
const TEXT_STEP = 12
const MAX_LEVEL = 2
// the rail overlaps the row above by BRIDGE px so the turn can straddle the boundary
const BRIDGE = 6
const levelOf = (depth: number) => Math.min(Math.max(depth - 2, 0), MAX_LEVEL)
const lineOffset = (depth: number) => BASE + levelOf(depth) * RAIL_STEP
const textOffset = (depth: number) => BASE + (levelOf(depth) + 1) * TEXT_STEP
const Rail = (props: { depth: number; prevDepth?: number; nextDepth?: number }) => {
const line = () => lineOffset(props.depth)
const prevLine = () => lineOffset(props.prevDepth ?? props.depth)
const nextLine = () => lineOffset(props.nextDepth ?? props.depth)
const turns = () => prevLine() !== line()
return (
)
}
export const WithRail = () => {
let contentRef: HTMLElement | null = null
return (
contentRef}>
(contentRef = el)}>
{items.map((item) => (
{item.label}
{[...Array(item.lines)].map(() => (
))}
))}
On this page
{items.map((item, index) => (
{item.label}
))}
)
}
```
### With Tree View
Pair `Toc.Root` with `TreeView` for hierarchical navigation. `onActiveChange` expands the branch holding the active
heading.
```tsx
import { Toc, useTocContext } from '@ark-ui/solid/toc'
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import tocStyles from 'styles/toc.module.css'
import treeStyles from 'styles/tree-view.module.css'
type TocNode = {
id: string
name: string
depth: number
lines?: number
children?: TocNode[]
}
const sections: TocNode[] = [
{
id: '09-guides',
name: 'Guides',
depth: 2,
lines: 10,
children: [
{ id: '09-quick-start', name: 'Quick Start', depth: 3, lines: 6 },
{ id: '09-manual-setup', name: 'Manual Setup', depth: 3, lines: 5 },
],
},
{
id: '09-core-concepts',
name: 'Core Concepts',
depth: 2,
lines: 9,
children: [
{ id: '09-toc-props', name: 'Props', depth: 3, lines: 7 },
{ id: '09-toc-events', name: 'Events', depth: 3, lines: 6 },
{ id: '09-toc-context', name: 'Context', depth: 3, lines: 8 },
],
},
{
id: '09-advanced',
name: 'Advanced',
depth: 2,
lines: 11,
children: [
{ id: '09-root-api', name: 'Root Provider', depth: 3, lines: 7 },
{ id: '09-custom-rendering', name: 'Custom Rendering', depth: 3, lines: 6 },
],
},
]
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: { id: 'ROOT', name: '', depth: 0, children: sections },
})
const allItems = sections.flatMap((section) => [
{ value: section.id, depth: section.depth },
...(section.children ?? []).map((child) => ({ value: child.id, depth: child.depth })),
])
const TocTreeNode = ({ node, indexPath }: TreeView.NodeProviderProps) => {
const toc = useTocContext()
return (
{node.children ? (
{node.name}
{node.children.map((child, index) => (
))}
) : (
{node.name}
)}
)
}
export const WithTreeView = () => {
const [expandedValue, setExpandedValue] = createSignal([])
let contentRef: HTMLElement | null = null
return (
contentRef}
onActiveChange={({ activeItems }) => {
const activeIds = new Set(activeItems.map((i) => i.value))
const next = sections
.filter(
(section) => activeIds.has(section.id) || (section.children ?? []).some((child) => activeIds.has(child.id)),
)
.map((s) => s.id)
setExpandedValue(next)
}}
>
(contentRef = el)}>
{sections.map((section) => (
{section.name}
{Array.from({ length: section.lines ?? 5 }).map(() => (
))}
{section.children?.map((child) => (
{child.name}
{Array.from({ length: child.lines ?? 3 }).map(() => (
))}
))}
))}
On this page
setExpandedValue(next)}
>
{sections.map((node, index) => (
))}
)
}
```
## Guides
### Items
Every entry needs `value`, the `id` of the heading element, and `depth`, the heading level.
```tsx
const items = [
{ value: 'introduction', depth: 2 },
{ value: 'installation', depth: 2 },
{ value: 'peer-dependencies', depth: 3 },
]
```
`value` must match the heading's `id` exactly. The component resolves it with `getElementById` to track visibility, and
`Toc.Link` targets it with `href="#introduction"`. An item whose id is missing renders but never activates.
Ids are global to the page, so prefix them when a page holds more than one TOC.
Extra properties are fine, a `label` for link text being the common one. `TocItemData` covers only `value` and `depth`,
so extend it rather than annotating with it directly:
```tsx
import type { TocItemData } from '@ark-ui/react/toc'
interface Item extends TocItemData {
label: string
}
```
## API Reference
### Props
### Root
#### Props
**`items`**
Type: `TocItem[]`
Required: true
Default Value: `undefined`
Description: The TOC items with `value` (slug/id) and `depth` (heading level).
**`activeIds`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled active heading ids.
**`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.
**`autoScroll`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to auto-scroll the TOC container so the first active item
is visible when active headings change.
**`defaultActiveIds`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The default active heading ids when rendered.
Use when you don't need to control the active headings.
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
root: string
title: string
list: string
item: (value: string) => string
link: (value: string) => string
indicator: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the TOC. Useful for composition.
**`onActiveChange`**
Type: `(details: ActiveChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback when the active (visible) headings change.
**`rootMargin`**
Type: `string`
Required: false
Default Value: `"-20px 0px -40% 0px"`
Description: The root margin for the IntersectionObserver.
Controls the effective viewport area for determining active headings.
**`scrollBehavior`**
Type: `ScrollBehavior`
Required: false
Default Value: `"smooth"`
Description: The default scroll behavior used when auto-scrolling the TOC container
and when scrolling to a heading (via link click or `api.scrollTo`).
Can be overridden per-call by passing `behavior` to `api.scrollTo`.
**`scrollEl`**
Type: `() => HTMLElement | null`
Required: false
Default Value: `undefined`
Description: Function that returns the scroll container element to observe within.
Defaults to the document/viewport.
**`threshold`**
Type: `number | number[]`
Required: false
Default Value: `0`
Description: The IntersectionObserver threshold. A value of `0` means the heading is
active as soon as even one pixel is visible within the root margin area.
### Content
#### Props
**`asChild`**
Type: `(props: ParentProps<'article'>) => 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.
### Item
#### Props
**`item`**
Type: `TocItem`
Required: true
Default Value: `undefined`
Description: The TOC item
**`asChild`**
Type: `(props: ParentProps<'li'>) => 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`**: toc
**`data-part`**: item
**`data-value`**: The value of the item
**`data-depth`**: The depth of the item
**`data-active`**: Present when active or pressed
**`data-first`**:
**`data-last`**:
### Link
#### Props
**`asChild`**
Type: `(props: ParentProps<'a'>) => 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`**: toc
**`data-part`**: link
**`data-value`**: The value of the item
**`data-active`**: Present when active or pressed
### List
#### Props
**`asChild`**
Type: `(props: ParentProps<'ul'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
### Nav
#### Props
**`asChild`**
Type: `(props: ParentProps<'nav'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
**`placement`**
Type: `'left' | 'right'`
Required: false
Default Value: `undefined`
Description: undefined
### RootProvider
#### Props
**`value`**
Type: `UseTocReturn`
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.
### Title
#### Props
**`asChild`**
Type: `(props: ParentProps<'h2'>) => 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 |
|----------|------|-------------|
| `activeIds` | `string[]` | All currently active (visible) heading ids |
| `activeItems` | `TocItem[]` | The active (visible) TOC items |
| `items` | `TocItem[]` | The resolved items list |
| `setActiveIds` | `(value: string[]) => void` | Manually set the active heading ids |
| `scrollTo` | `(value: string, details?: ScrollToDetails | undefined) => void` | Scrolls to the heading with the given id. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a TOC item |
# Toast
## Anatomy
```tsx
const toaster = createToaster({ placement: 'bottom-end' })
{(toast) => (
)}
```
## Setup
To use the Toast component, create the toast engine using the `createToaster` function.
This function manages the placement and grouping of toasts, and provides a `toast` object needed to create toast
notification.
```ts
const toaster = createToaster({
placement: 'bottom-end',
overlap: true,
gap: 24,
})
```
## Examples
Here's an example of creating a toast using the `toast.create` method.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
export const Basic = () => {
const toaster = createToaster({
placement: 'bottom-end',
overlap: true,
gap: 24,
})
return (
toaster.create({
title: 'Scheduled for tomorrow',
description: 'Your meeting has been scheduled for tomorrow at 10am.',
type: 'info',
})
}
>
Schedule meeting
{(toast) => (
{toast().title}
{toast().description}
)}
)
}
```
### Types
You can create different types of toasts (`success`, `error`, `warning`, `info`) with appropriate styling. For example,
to create a success toast, you can do:
```ts
toaster.success({
title: 'Success!',
description: 'Your changes have been saved.',
})
```
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, XIcon } from 'lucide-solid'
import { Dynamic } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
const iconMap = {
success: CircleCheckIcon,
error: CircleAlertIcon,
warning: TriangleAlertIcon,
info: InfoIcon,
}
export const Types = () => {
const toaster = createToaster({
overlap: true,
placement: 'bottom-end',
gap: 16,
})
return (
toaster.success({ title: 'Changes saved', description: 'Your profile has been updated successfully.' })
}
>
Success
toaster.error({ title: 'Upload failed', description: 'There was an error uploading your file.' })
}
>
Error
toaster.warning({ title: 'Low storage', description: 'You have less than 10% storage remaining.' })
}
>
Warning
toaster.info({ title: 'Update available', description: 'A new version of the app is ready to install.' })
}
>
Info
{(toast) => {
const icon = () => (toast().type ? iconMap[toast().type as keyof typeof iconMap] : InfoIcon)
return (
{toast().title}
{toast().description}
)
}}
)
}
```
### Promise
You can use `toaster.promise()` to automatically handle the different states of an asynchronous operation. It provides
options for the `success`, `error`, and `loading` states of the promise and will automatically update the toast when the
promise resolves or rejects.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, LoaderIcon, XIcon } from 'lucide-solid'
import { Dynamic } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
const uploadFile = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve() : reject(new Error('Upload failed'))
}, 2000)
})
}
const iconMap = {
loading: LoaderIcon,
success: CircleCheckIcon,
error: CircleAlertIcon,
info: InfoIcon,
}
export const PromiseToast = () => {
const toaster = createToaster({
overlap: true,
placement: 'bottom-end',
gap: 16,
})
const handleUpload = async () => {
toaster.promise(uploadFile, {
loading: {
title: 'Uploading file...',
description: 'Please wait while we process your file.',
},
success: {
title: 'Upload complete',
description: 'Your file has been uploaded successfully.',
},
error: {
title: 'Upload failed',
description: 'There was an error uploading your file. Please try again.',
},
})
}
return (
Upload file
{(toast) => {
const icon = () => (toast().type ? iconMap[toast().type as keyof typeof iconMap] : InfoIcon)
return (
{toast().title}
{toast().description}
)
}}
)
}
```
### Update
To update a toast, use the `toast.update` method.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
export const Update = () => {
const toaster = createToaster({
placement: 'bottom-end',
overlap: true,
gap: 16,
})
const [id, setId] = createSignal(undefined)
const createToast = () => {
const newId = toaster.create({
title: 'Uploading file...',
description: 'Please wait while your file is being uploaded.',
type: 'loading',
})
setId(newId)
}
const updateToast = () => {
const currentId = id()
if (!currentId) {
return
}
toaster.update(currentId, {
title: 'Upload complete',
description: 'Your file has been uploaded successfully.',
type: 'success',
})
}
return (
Start upload
Complete upload
{(toast) => (
{toast().title}
{toast().description}
)}
)
}
```
### Action
To add an action to a toast, use the `toast.action` property.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
export const Action = () => {
const toaster = createToaster({
placement: 'bottom-end',
overlap: true,
gap: 16,
})
return (
toaster.create({
title: 'Invitation sent',
description: 'Your team invitation has been sent. Click undo to cancel.',
type: 'info',
action: {
label: 'Undo',
onClick: () => {
console.log('Undo clicked')
},
},
})
}
>
Send invitation
{(toast) => (
{toast().title}
{toast().description}
{toast().action && (
{toast().action?.label}
)}
)}
)
}
```
### Duration
You can control how long a toast stays visible by setting a custom `duration` in milliseconds, or use `Infinity` to keep
it visible until manually dismissed.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
const durations = [
{ label: '1s', value: 1000 },
{ label: '3s', value: 3000 },
{ label: '5s', value: 5000 },
{ label: '∞', value: Infinity },
]
export const Duration = () => {
const toaster = createToaster({
overlap: true,
placement: 'bottom-end',
gap: 16,
})
return (
{(duration) => (
toaster.create({
title: `Duration: ${duration.label}`,
description:
duration.value === Infinity
? 'This toast will stay until you dismiss it.'
: `This toast will automatically close in ${duration.label}.`,
type: 'info',
duration: duration.value,
})
}
>
{duration.label}
)}
{(toast) => (
{toast().title}
{toast().description}
)}
)
}
```
### Max Visible
Set the `max` prop on the `createToaster` function to define the maximum number of toasts that can be rendered at any
one time. Any extra toasts will be queued and rendered when a toast has been dismissed.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
export const MaxToasts = () => {
const toaster = createToaster({
max: 3,
overlap: true,
placement: 'bottom-end',
gap: 16,
})
return (
toaster.create({
title: 'New notification',
description: 'Maximum of 3 toasts visible at once. Extra toasts are queued.',
type: 'info',
})
}
>
Add toast
{
const messages = [
{ title: 'Message received', description: 'You have a new message from Sarah.' },
{ title: 'File uploaded', description: 'Your document has been saved.' },
{ title: 'Sync complete', description: 'All changes have been synced.' },
{ title: 'New follower', description: 'John started following you.' },
{ title: 'Task completed', description: 'Your export is ready for download.' },
]
messages.forEach((msg) => {
toaster.create({
title: msg.title,
description: msg.description,
type: 'info',
})
})
}}
>
Add 5 toasts
{(toast) => (
{toast().title}
{toast().description}
)}
)
}
```
### Placement
Configure where toasts appear on the screen using the `placement` option in `createToaster`. Options include
`top-start`, `top-end`, `bottom-start`, `bottom-end`, and more.
```tsx
import { Portal } from 'solid-js/web'
import { Toast, Toaster, createToaster } from '@ark-ui/solid/toast'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'
export const Placement = () => {
const toaster = createToaster({
placement: 'top-end',
overlap: true,
gap: 16,
})
return (
toaster.create({
title: 'Notification',
description: 'This toast appears at the top-right corner.',
type: 'info',
})
}
>
Show toast (top-end)
{(toast) => (
{toast().title}
{toast().description}
)}
)
}
```
## Guides
### Toast in Effects
When creating a toast inside React effects (like `useEffect`, `useLayoutEffect`, or event handlers that trigger during
render), you may encounter a "flushSync" warning. To avoid this, wrap the toast call in `queueMicrotask`:
```tsx
import { useEffect } from 'react'
export const EffectToast = () => {
useEffect(() => {
// ❌ This may cause flushSync warnings
// toaster.create({ title: 'Effect triggered!' })
// ✅ Wrap in queueMicrotask to avoid warnings
queueMicrotask(() => {
toaster.create({
title: 'Effect triggered!',
description: 'This toast was called safely from an effect',
type: 'success',
})
})
}, [])
return Component content
}
```
This ensures the toast creation is deferred until after the current execution context, preventing React's concurrent
rendering warnings.
### Styling
There's a minimal styling required for the toast to work correctly.
#### Toast root
The toast root will be assigned these css properties at runtime:
- `--x` - The x position
- `--y` - The y position
- `--scale` - The scale
- `--z-index` - The z-index
- `--height` - The height
- `--opacity` - The opacity
- `--gap` - The gap between toasts
```css
[data-scope='toast'][data-part='root'] {
translate: var(--x) var(--y);
scale: var(--scale);
z-index: var(--z-index);
height: var(--height);
opacity: var(--opacity);
will-change: translate, opacity, scale;
transition:
translate 400ms,
scale 400ms,
opacity 400ms,
height 400ms,
box-shadow 200ms;
transition-timing-function: cubic-bezier(0.21, 1.02, 0.73, 1);
&[data-state='closed'] {
transition:
translate 400ms,
scale 400ms,
opacity 200ms;
transition-timing-function: cubic-bezier(0.06, 0.71, 0.55, 1);
}
}
```
#### Styling based on type
You can also style based on the `data-type` attribute.
```css
[data-scope='toast'][data-part='root'] {
&[data-type='error'] {
background: red;
color: white;
}
&[data-type='info'] {
background: blue;
color: white;
}
&[data-type='warning'] {
background: orange;
}
&[data-type='success'] {
background: green;
color: white;
}
}
```
#### Mobile considerations
A very common use case is to adjust the toast width on mobile so it spans the full width of the screen.
```css
@media (max-width: 640px) {
[data-scope='toast'][data-part='group'] {
width: 100%;
}
[data-scope='toast'][data-part='root'] {
inset-inline: 0;
width: calc(100% - var(--gap) * 2);
}
}
```
## 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.
#### Data Attributes
**`data-scope`**: toast
**`data-part`**: root
**`data-state`**: "open" | "closed"
**`data-type`**: The type of the item
**`data-placement`**: The placement of the toast
**`data-align`**:
**`data-side`**: The side of the trigger that the toast is positioned on
**`data-mounted`**: Present when mounted
**`data-paused`**: Present when paused
**`data-first`**:
**`data-sibling`**:
**`data-stack`**:
**`data-overlap`**: Present when overlapping
### ActionTrigger
#### 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.
### CloseTrigger
#### 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.
### Description
#### 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.
### Store
#### Props
**`duration`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The duration of the toast.
By default, it is determined by the type of the toast.
**`gap`**
Type: `number`
Required: false
Default Value: `16`
Description: The gap between the toasts
**`hotkey`**
Type: `string[]`
Required: false
Default Value: `'["altKey", "KeyT"]'`
Description: The hotkey that will move focus to the toast group
**`max`**
Type: `number`
Required: false
Default Value: `24`
Description: The maximum number of toasts. When the number of toasts exceeds this limit, the new toasts are queued.
**`offsets`**
Type: `string | Record<'top' | 'bottom' | 'left' | 'right', string>`
Required: false
Default Value: `"1rem"`
Description: The offset from the safe environment edge of the viewport
**`overlap`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to overlap the toasts
**`pauseOnPageIdle`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to pause toast when the user leaves the browser tab
**`placement`**
Type: `Placement`
Required: false
Default Value: `"bottom"`
Description: The placement of the toast
**`removeDelay`**
Type: `number`
Required: false
Default Value: `200`
Description: The duration for the toast to kept alive before it is removed.
Useful for exit transitions.
### Title
#### 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.
### Toaster
#### Props
**`toaster`**
Type: `CreateToasterReturn`
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.
**`dir`**
Type: `'ltr' | 'rtl'`
Required: false
Default Value: `"ltr"`
Description: The document's text/writing direction.
**`getRootNode`**
Type: `() => Node | ShadowRoot | Document`
Required: false
Default Value: `undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `getCount` | `() => number` | The total number of toasts |
| `getToasts` | `() => ToastProps[]` | The toasts |
| `subscribe` | `(callback: (toasts: Options[]) => void) => VoidFunction` | Subscribe to the toast group |
# Toggle
## Anatomy
```tsx
```
## Examples
```tsx
import { Toggle } from '@ark-ui/solid/toggle'
import { BoldIcon } from 'lucide-solid'
import styles from 'styles/toggle.module.css'
export const Basic = () => {
return (
)
}
```
### Controlled
Use the `pressed` and `onPressedChange` props to control the toggle's state.
```tsx
import { Toggle } from '@ark-ui/solid/toggle'
import { HeartIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/toggle.module.css'
export const Controlled = () => {
const [pressed, setPressed] = createSignal(false)
return (
}>
)
}
```
### Disabled
Use the `disabled` prop to disable the toggle.
```tsx
import { Toggle } from '@ark-ui/solid/toggle'
import { BoldIcon } from 'lucide-solid'
import styles from 'styles/toggle.module.css'
export const Disabled = () => {
return (
)
}
```
### Indicator
Use the `Toggle.Indicator` component to render different indicators based on the state of the toggle.
```tsx
import { Toggle } from '@ark-ui/solid/toggle'
import { HeartIcon } from 'lucide-solid'
import styles from 'styles/toggle.module.css'
export const Indicator = () => {
return (
}>
)
}
```
## API Reference
### Props
### Root
#### 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.
**`defaultPressed`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The default pressed state of the toggle.
**`onPressedChange`**
Type: `(pressed: boolean) => void`
Required: false
Default Value: `undefined`
Description: Event handler called when the pressed state of the toggle changes.
**`pressed`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The pressed state of the toggle.
#### Data Attributes
**`data-scope`**: toggle
**`data-part`**: root
**`data-state`**: "on" | "off"
**`data-pressed`**: Present when pressed
**`data-disabled`**: Present when disabled
### 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.
**`fallback`**
Type: `number | boolean | Node | ArrayElement | (string & {})`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: toggle
**`data-part`**: indicator
**`data-disabled`**: Present when disabled
**`data-pressed`**: Present when pressed
**`data-state`**: "on" | "off"
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `pressed` | `boolean` | Whether the toggle is pressed. |
| `disabled` | `boolean` | Whether the toggle is disabled. |
| `setPressed` | `(pressed: boolean) => void` | Sets the pressed state of the toggle. |
| `getRootProps` | `() => T["element"]` | Props for the root `button` element. |
| `getIndicatorProps` | `() => T["element"]` | Props for the optional visual indicator (e.g. icon) element. |
# Toggle Group
## Anatomy
```tsx
```
## Examples
```tsx
import { ToggleGroup } from '@ark-ui/solid/toggle-group'
import { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon } from 'lucide-solid'
import styles from 'styles/toggle-group.module.css'
export const Basic = () => {
return (
)
}
```
### Controlled
Use the `value` and `onValueChange` props to control the toggle group state.
```tsx
import { ToggleGroup } from '@ark-ui/solid/toggle-group'
import { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/toggle-group.module.css'
export const Controlled = () => {
const [value, setValue] = createSignal(['left'])
return (
setValue(e.value)} class={styles.Root}>
)
}
```
### Root Provider
An alternative way to control the toggle group is to use the `RootProvider` component and the `useToggleGroup` hook.
This way you can access the state and methods from outside the component.
```tsx
import { ToggleGroup, useToggleGroup } from '@ark-ui/solid/toggle-group'
import { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon } from 'lucide-solid'
import styles from 'styles/toggle-group.module.css'
export const RootProvider = () => {
const toggleGroup = useToggleGroup({ defaultValue: ['left'] })
return (
<>
Selected: {String(toggleGroup().value)}
>
)
}
```
### Multiple
Demonstrates how to enable `multiple` selection within the group.
```tsx
import { ToggleGroup } from '@ark-ui/solid/toggle-group'
import { BoldIcon, ItalicIcon, UnderlineIcon } from 'lucide-solid'
import styles from 'styles/toggle-group.module.css'
export const Multiple = () => {
return (
)
}
```
### With Tooltip
Pair an item with a `Tooltip.Trigger` via `asChild`, matching their ids with `ids.item` and `ids.trigger` so they share
one element and tab stop.
```tsx
import { ToggleGroup } from '@ark-ui/solid/toggle-group'
import { Tooltip, useTooltip } from '@ark-ui/solid/tooltip'
import { BoldIcon, ItalicIcon, UnderlineIcon } from 'lucide-solid'
import { For } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/toggle-group.module.css'
import tooltipStyles from 'styles/tooltip.module.css'
const items = [
{ value: 'bold', label: 'Bold', icon: BoldIcon },
{ value: 'italic', label: 'Italic', icon: ItalicIcon },
{ value: 'underline', label: 'Underline', icon: UnderlineIcon },
]
const getTriggerId = (value?: string) => `toggle-item:${value}`
export const WithTooltip = () => {
const tooltip = useTooltip({ ids: { trigger: getTriggerId } })
return (
{(item) => (
(
)}
/>
)}
{items.find((item) => item.value === tooltip().triggerValue)?.label}
)
}
```
## 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 selected value of the toggle group when rendered.
Use when you don't need to control the selected value of the toggle group.
**`deselectable`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the toggle group allows empty selection.
**Note:** This is ignored if `multiple` is `true`.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the toggle is disabled.
**`ids`**
Type: `Partial<{ root: string; item: (value: string) => string }>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the toggle. Useful for composition.
**`loopFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to loop focus inside the toggle group.
**`multiple`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow multiple toggles to be selected.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to call when the toggle is clicked.
**`orientation`**
Type: `Orientation`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the toggle group.
**`rovingFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to use roving tab index to manage focus.
**`value`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled selected value of the toggle group.
#### Data Attributes
**`data-scope`**: toggle-group
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the toggle-group
**`data-focus`**: Present when focused
### Item
#### Props
**`value`**
Type: `string`
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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
#### Data Attributes
**`data-scope`**: toggle-group
**`data-part`**: item
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the item
**`data-state`**: "on" | "off"
### RootProvider
#### Props
**`value`**
Type: `UseToggleGroupReturn`
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 |
|----------|------|-------------|
| `value` | `string[]` | The value of the toggle group. |
| `setValue` | `(value: string[]) => void` | Sets the value of the toggle group. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of the toggle item. |
## Accessibility
### Keyboard Support
**`Tab`**
Description: Moves focus to either the pressed item or the first item in the group.
**`Space`**
Description: Activates/deactivates the item.
**`Enter`**
Description: Activates/deactivates the item.
**`ArrowDown`**
Description: Moves focus to the next item in the group.
**`ArrowRight`**
Description: Moves focus to the next item in the group.
**`ArrowUp`**
Description: Moves focus to the previous item in the group.
**`ArrowLeft`**
Description: Moves focus to the previous item in the group.
**`Home`**
Description: Moves focus to the first item.
**`End`**
Description: Moves focus to the last item.
# Tooltip
## Anatomy
```tsx
```
## Examples
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Basic = () => (
Hover Me
I am a tooltip!
)
```
### Controlled
To create a controlled Tooltip component, manage the state of whether the tooltip is open using the `open` prop:
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Controlled = () => {
const [open, setOpen] = createSignal(false)
return (
<>
setOpen(!open())}>
Toggle
setOpen(e.open)}>
Hover Me
I am a tooltip!
>
)
}
```
### Root Provider
An alternative way to control the tooltip is to use the `RootProvider` component and the `useTooltip` hook. This way you
can access the state and methods from outside the component.
```tsx
import { Tooltip, useTooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const RootProvider = () => {
const tooltip = useTooltip()
return (
<>
tooltip().setOpen(true)}>Open
Hover Me
I am a tooltip!
>
)
}
```
### Arrow
To display an arrow pointing to the trigger from the tooltip, use the `Tooltip.Arrow` and `Tooltip.ArrowTip` components:
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Arrow = () => (
Hover Me
I am a tooltip!
)
```
### Delay
To configure the open and close delay for the Tooltip, use the `closeDelay` and `openDelay` props:
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Delay = () => (
Hover Me
I am a tooltip!
)
```
### Positioning
To customize the position of the Tooltip relative to the trigger, use the `positioning` prop:
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Positioning = () => (
Hover Me
I am a tooltip!
)
```
### Context
Access the tooltip's state and methods with `Tooltip.Context` or the `useTooltipContext` hook:
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const Context = () => (
Hover Me
{(context) => (
This tooltip is open: {context().open.toString()}
)}
)
```
### Within Fixed Containers
When rendering a tooltip inside a fixed-position container, set `positioning.strategy` to `"fixed"` to ensure proper
positioning.
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
export const WithinFixed = () => (
)
```
### Multiple Triggers
Share a single tooltip across multiple trigger elements. Pass a `value` to each `Tooltip.Trigger` — the tooltip
repositions to whichever trigger is hovered without closing.
```tsx
import { Tooltip } from '@ark-ui/solid/tooltip'
import { BoldIcon, ItalicIcon, StrikethroughIcon, UnderlineIcon } from 'lucide-solid'
import { type Component, For, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/tooltip.module.css'
interface Tool {
id: string
label: string
shortcut: string
icon: Component
}
const tools: Tool[] = [
{ id: 'bold', label: 'Bold', shortcut: '⌘+B', icon: BoldIcon },
{ id: 'italic', label: 'Italic', shortcut: '⌘+I', icon: ItalicIcon },
{ id: 'underline', label: 'Underline', shortcut: '⌘+U', icon: UnderlineIcon },
{ id: 'strikethrough', label: 'Strikethrough', shortcut: '⌘+⇧+X', icon: StrikethroughIcon },
]
export const MultipleTriggers = () => {
const [activeTool, setActiveTool] = createSignal(null)
return (
{
setActiveTool(tools.find((t) => t.id === e.value) ?? null)
}}
>
{(tool) => (
)}
{activeTool() && (
<>
{activeTool()!.label} {activeTool()!.shortcut}
>
)}
)
}
```
## API Reference
### Props
### Root
#### Props
**`aria-label`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Custom label for the tooltip.
**`closeDelay`**
Type: `number`
Required: false
Default Value: `150`
Description: The close delay of the tooltip.
**`closeOnClick`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the tooltip should close on click
**`closeOnEscape`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to close the tooltip when the Escape key is pressed.
**`closeOnPointerDown`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether to close the tooltip on pointerdown.
**`closeOnScroll`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the tooltip should close on scroll
**`defaultOpen`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The initial open state of the tooltip when rendered.
Use when you don't need to control the open state of the tooltip.
**`defaultTriggerValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial trigger value when rendered.
Use when you don't need to control the trigger value.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tooltip is disabled
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{
trigger: string | ((value?: string | undefined) => string)
content: string
arrow: string
positioner: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the tooltip. 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
**`interactive`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether the tooltip's content is interactive.
In this mode, the tooltip will remain open when user hovers over the content.
**`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
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the tooltip is opened.
**`onTriggerValueChange`**
Type: `(details: TriggerValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the trigger value changes.
**`open`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: The controlled open state of the tooltip
**`openDelay`**
Type: `number`
Required: false
Default Value: `400`
Description: The open delay of the tooltip.
**`positioning`**
Type: `PositioningOptions`
Required: false
Default Value: `undefined`
Description: The user provided options used to position the popover content
**`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.
**`triggerValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled trigger value
**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.
### Arrow
#### 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.
### ArrowTip
#### 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.
### 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`**: tooltip
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-instant`**:
**`data-placement`**: The placement of the content
**`data-side`**: The side of the trigger that the content is positioned on
### 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: `UseTooltipReturn`
Required: true
Default Value: `undefined`
Description: undefined
**`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.
**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The value that identifies this specific trigger
#### Data Attributes
**`data-scope`**: tooltip
**`data-part`**: trigger
**`data-value`**: The value of the item
**`data-current`**: Present when current
**`data-expanded`**: Present when expanded
**`data-state`**: "open" | "closed"
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `open` | `boolean` | Whether the tooltip is open. |
| `setOpen` | `(open: boolean) => void` | Function to open the tooltip. |
| `triggerValue` | `string | null` | The trigger value |
| `setTriggerValue` | `(value: string | null) => void` | Function to set the trigger value |
| `reposition` | `(options?: Partial) => void` | Function to reposition the popover |
## Accessibility
Complies with the [Tooltip WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/).
### Keyboard Support
**`Tab`**
Description: Opens/closes the tooltip without delay.
**`Escape`**
Description: If open, closes the tooltip without delay.
# Tour
## Anatomy
```tsx
const tour = useTour({ steps: [...] })
```
## Examples
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { MoreHorizontalIcon, SaveIcon, SparklesIcon, UploadIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'welcome',
type: 'dialog',
title: 'Welcome to the App!',
description: "Let's take a quick tour to get you started with the main features.",
actions: [{ label: 'Start Tour', action: 'next' }],
},
{
id: 'upload',
type: 'tooltip',
title: 'Upload Files',
description: 'Click here to upload your files to the cloud.',
target: () => document.querySelector('#btn-upload'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'save',
type: 'tooltip',
title: 'Save Changes',
description: 'Save your work to keep your progress.',
target: () => document.querySelector('#btn-save'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'more',
type: 'tooltip',
title: 'More Options',
description: 'Access additional settings and actions from this menu.',
target: () => document.querySelector('#btn-more'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'complete',
type: 'dialog',
title: "You're all set!",
description: 'You now know the basics. Enjoy using the app!',
actions: [{ label: 'Finish', action: 'dismiss' }],
},
]
export const Basic = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Tour
Upload
Save
More
{(actions) => (
{(action) => }
)}
)
}
```
### Step Types
Demonstrate all three step types in a single tour: `dialog` for welcome/completion, `tooltip` anchored to elements, and
`floating` for fixed-position content.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'welcome',
type: 'dialog',
title: 'Welcome!',
description: 'This tour demonstrates different step types: dialog, tooltip, and floating.',
actions: [{ label: 'Start Tour', action: 'next' }],
},
{
id: 'tooltip-step',
type: 'tooltip',
title: 'Tooltip Step',
description: 'This step appears as a tooltip anchored to a specific element.',
target: () => document.querySelector('#target-element'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'floating-step',
type: 'floating',
placement: 'bottom-end',
title: 'Floating Step',
description: 'This step floats at a fixed position on the screen, independent of any target.',
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'complete',
type: 'dialog',
title: 'Tour Complete!',
description: 'You have seen all the different step types available.',
actions: [{ label: 'Done', action: 'dismiss' }],
},
]
export const MixedTypes = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Tour
Target Element
{(actions) => (
{(action) => }
)}
)
}
```
### Progress
Display a visual progress indicator at the bottom of the tour content showing how far along the user is.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
title: 'Progress Tracking',
description: 'Watch the progress bar at the bottom as you navigate.',
target: () => document.querySelector('#progress-1'),
actions: [{ label: 'Next', action: 'next' }],
},
{
id: 'step-2',
type: 'tooltip',
title: 'Halfway There',
description: 'The progress bar shows how far along you are.',
target: () => document.querySelector('#progress-2'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-3',
type: 'tooltip',
title: 'Almost Done',
description: 'One more step to complete the tour.',
target: () => document.querySelector('#progress-3'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-4',
type: 'tooltip',
title: 'Complete!',
description: 'You have completed all the steps.',
target: () => document.querySelector('#progress-4'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Finish', action: 'dismiss' },
],
},
]
export const ProgressBar = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Tour
{(actions) => (
{(action) => }
)}
)
}
```
### Skip
Allow users to skip the entire tour at any step by adding a skip action.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
title: 'First Feature',
description: 'You can skip this tour at any time using the Skip button.',
target: () => document.querySelector('#item-1'),
actions: [
{ label: 'Skip', action: 'dismiss' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-2',
type: 'tooltip',
title: 'Second Feature',
description: 'Continue or skip to end the tour early.',
target: () => document.querySelector('#item-2'),
actions: [
{ label: 'Skip', action: 'dismiss' },
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-3',
type: 'tooltip',
title: 'Final Feature',
description: 'This is the last step of the tour.',
target: () => document.querySelector('#item-3'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Finish', action: 'dismiss' },
],
},
]
export const SkipTour = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Tour
{(actions) => (
{(action) => }
)}
)
}
```
### Keyboard Navigation
Enable arrow key navigation between tour steps using the `keyboardNavigation` prop.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { KeyboardIcon, SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
title: 'Keyboard Navigation',
description: 'Press the right arrow key to go to the next step.',
target: () => document.querySelector('#key-1'),
actions: [{ label: 'Next', action: 'next' }],
},
{
id: 'step-2',
type: 'tooltip',
title: 'Go Back',
description: 'Press the left arrow key to go back.',
target: () => document.querySelector('#key-2'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-3',
type: 'tooltip',
title: 'Close Tour',
description: 'Press Escape to close the tour at any time.',
target: () => document.querySelector('#key-3'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Finish', action: 'dismiss' },
],
},
]
export const KeyboardNavigation = () => {
const tour = useTour({ steps, keyboardNavigation: true })
return (
tour().start()}>
Start Tour
Use arrow keys to navigate, Escape to close
{(actions) => (
{(action) => }
)}
)
}
```
### Events
Listen to tour lifecycle events like `onStepChange` and `onStatusChange` to track user progress.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
title: 'First Step',
description: 'Watch the event log below as you navigate.',
target: () => document.querySelector('#event-1'),
actions: [{ label: 'Next', action: 'next' }],
},
{
id: 'step-2',
type: 'tooltip',
title: 'Second Step',
description: 'Each step change triggers an event.',
target: () => document.querySelector('#event-2'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
id: 'step-3',
type: 'tooltip',
title: 'Final Step',
description: 'Complete the tour to see the status change.',
target: () => document.querySelector('#event-3'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Finish', action: 'dismiss' },
],
},
]
export const Events = () => {
const [logs, setLogs] = createSignal([])
const addLog = (message: string) => {
setLogs((prev) => [...prev, message])
}
const tour = useTour({
steps,
onStepChange(details) {
addLog(`Step changed: ${details.stepId}`)
},
onStatusChange(details) {
addLog(`Status: ${details.status}`)
},
})
return (
tour().start()}>
Start Tour
Event Log:
0} fallback={Start the tour to see events
}>
{(log) => {log}
}
{(actions) => (
{(action) => }
)}
)
}
```
### Wait for Click
Use the `effect` function with `waitForEvent` to wait for user interaction before proceeding to the next step.
```tsx
import { Tour, useTour, waitForEvent } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'intro',
type: 'dialog',
title: 'Interactive Tutorial',
description: 'This tour will guide you through actions. You must complete each step to proceed.',
actions: [{ label: 'Begin', action: 'next' }],
},
{
id: 'click-add',
type: 'tooltip',
title: 'Click the Add Button',
description: 'Click the "Add Item" button to continue.',
target: () => document.querySelector('#btn-add'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'click')
promise.then(() => next())
return cancel
},
},
{
id: 'click-edit',
type: 'tooltip',
title: 'Click the Edit Button',
description: 'Now click the "Edit" button.',
target: () => document.querySelector('#btn-edit'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'click')
promise.then(() => next())
return cancel
},
},
{
id: 'click-delete',
type: 'tooltip',
title: 'Click the Delete Button',
description: 'Finally, click the "Delete" button.',
target: () => document.querySelector('#btn-delete'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'click')
promise.then(() => next())
return cancel
},
},
{
id: 'complete',
type: 'dialog',
title: 'Well Done!',
description: 'You completed all the interactive steps.',
actions: [{ label: 'Finish', action: 'dismiss' }],
},
]
export const WaitForClick = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Interactive Tour
Add Item
Edit
Delete
{(actions) => (
{(action) => }
)}
)
}
```
### Wait for Input
Create form tutorials that wait for users to enter valid input before advancing.
```tsx
import { Tour, useTour, waitForEvent } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'intro',
type: 'dialog',
title: 'Form Tutorial',
description: 'Learn how to fill out the form by following the guided steps.',
actions: [{ label: 'Start', action: 'next' }],
},
{
id: 'enter-name',
type: 'tooltip',
title: 'Enter Your Name',
description: 'Type your name in the input field to continue.',
target: () => document.querySelector('#input-name'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'input', {
predicate: (el) => el.value.trim().length >= 2,
})
promise.then(() => next())
return cancel
},
},
{
id: 'enter-email',
type: 'tooltip',
title: 'Enter Your Email',
description: 'Now enter a valid email address.',
target: () => document.querySelector('#input-email'),
effect({ next, target, show }) {
show()
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const [promise, cancel] = waitForEvent(target, 'input', {
predicate: (el) => emailRegex.test(el.value),
})
promise.then(() => next())
return cancel
},
},
{
id: 'check-terms',
type: 'tooltip',
title: 'Accept Terms',
description: 'Check the checkbox to accept the terms.',
target: () => document.querySelector('#checkbox-terms'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'change', {
predicate: (el) => el.checked,
})
promise.then(() => next())
return cancel
},
},
{
id: 'complete',
type: 'dialog',
title: 'Form Complete!',
description: 'You have successfully filled out the form.',
actions: [{ label: 'Done', action: 'dismiss' }],
},
]
export const WaitForInput = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Form Tutorial
{(actions) => (
{(action) => }
)}
)
}
```
### Wait for Element
Wait for dynamically rendered elements to appear in the DOM before showing a step.
```tsx
import { Tour, useTour, waitForElement, waitForEvent } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { PlusIcon, SparklesIcon, XIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'intro',
type: 'dialog',
title: 'Dynamic Elements',
description: 'This tour demonstrates waiting for elements that appear dynamically.',
actions: [{ label: 'Start', action: 'next' }],
},
{
id: 'add-item',
type: 'tooltip',
title: 'Add an Item',
description: 'Click the button to add a new item to the list.',
target: () => document.querySelector('#btn-add-item'),
effect({ next, target, show }) {
show()
const [promise, cancel] = waitForEvent(target, 'click')
promise.then(() => next())
return cancel
},
},
{
id: 'new-item',
type: 'tooltip',
title: 'New Item Added!',
description: 'The tour waited for this element to appear before showing this step.',
target: () => document.querySelector('[data-item="new"]'),
effect({ show }) {
const [promise, cancel] = waitForElement(() => document.querySelector('[data-item="new"]'), {
timeout: 5000,
})
promise.then(() => show())
return () => cancel()
},
actions: [{ label: 'Next', action: 'next' }],
},
{
id: 'complete',
type: 'dialog',
title: 'Tour Complete',
description: 'You learned how to use waitForElement for dynamic content.',
actions: [{ label: 'Done', action: 'dismiss' }],
},
]
export const WaitForElement = () => {
const tour = useTour({ steps })
const [items, setItems] = createSignal(['Item 1', 'Item 2'])
const addItem = () => {
setItems((prev) => [...prev, `Item ${prev.length + 1}`])
}
return (
tour().start()}>
Start Tour
Add Item
{(item, index) => (
2 ? 'new' : undefined}
>
{item}
)}
{(actions) => (
{(action) => }
)}
)
}
```
### Async
Load data asynchronously and update step content before displaying it using the `effect` function with `show()` and
`update()`.
```tsx
import { Tour, useTour } from '@ark-ui/solid/tour'
import { Portal } from 'solid-js/web'
import { SparklesIcon, XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tour.module.css'
const steps: Tour.StepDetails[] = [
{
id: 'intro',
type: 'dialog',
title: 'Async Data Loading',
description: 'This tour demonstrates loading data before showing a step.',
actions: [{ label: 'Next', action: 'next' }],
},
{
id: 'user-info',
type: 'tooltip',
title: 'Loading...',
description: 'Fetching user data...',
target: () => document.querySelector('#user-card'),
actions: [
{ label: 'Back', action: 'prev' },
{ label: 'Next', action: 'next' },
],
effect({ show, update }) {
const controller = new AbortController()
fetch('https://api.github.com/users/segunadebayo', { signal: controller.signal })
.then((res) => res.json())
.then((data) => {
update({
title: `Welcome, ${data.name || data.login}!`,
description: `You have ${data.public_repos} public repositories and ${data.followers} followers.`,
})
show()
})
.catch(() => {
update({
title: 'User Profile',
description: 'Could not load user data. Please try again.',
})
show()
})
return () => controller.abort()
},
},
{
id: 'complete',
type: 'dialog',
title: 'Tour Complete',
description: 'The async step loaded data from the GitHub API before displaying.',
actions: [{ label: 'Done', action: 'dismiss' }],
},
]
export const AsyncStep = () => {
const tour = useTour({ steps })
return (
tour().start()}>
Start Tour
User Profile Card
{(actions) => (
{(action) => }
)}
)
}
```
## Guides
### Step Types
The tour machine supports different types of steps, allowing you to create a diverse and interactive tour experience.
The available step types are defined in the `StepType` type:
- `tooltip`: Displays the step content as a tooltip, typically positioned near the target element.
- `dialog`: Shows the step content in a modal dialog centered on screen, useful for starting or ending the tour. This
usually don't have a `target` defined.
- `floating`: Presents the step content as a floating element, which can be positioned flexibly on the screen. This
usually don't have a `target` defined.
- `wait`: A special type that waits for a specific condition before proceeding to the next step.
```tsx
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
placement: 'top-start',
target: () => document.querySelector('#target-1'),
title: 'Tooltip Step',
description: 'This is a tooltip step',
},
{
id: 'step-2',
type: 'dialog',
title: 'Dialog Step',
description: 'This is a dialog step',
},
{
id: 'step-3',
type: 'floating',
placement: 'top-start',
title: 'Floating Step',
description: 'This is a floating step',
},
{
id: 'step-4',
type: 'wait',
title: 'Wait Step',
description: 'This is a wait step',
effect({ next }) {
// do something and go next
// you can also return a cleanup
},
},
]
```
### Actions
Every step supports a list of actions that are rendered in the step footer.Use the `actions` property to define each
action.
```tsx
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'dialog',
title: 'Dialog Step',
description: 'This is a dialog step',
actions: [{ label: 'Show me a tour!', action: 'next' }],
},
]
```
### Tooltip Placement
Use the `placement` property to define the placement of the tooltip.
```tsx {5}
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
placement: 'top-start',
// ...
},
]
```
### Hide Arrow
Set `arrow: false` in the step property to hide the tooltip arrow. This is only useful for tooltip steps.
```tsx {5}
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
arrow: false,
},
]
```
### Hide Backdrop
Set `backdrop: false` in the step property to hide the backdrop. This applies to all step types except the `wait` step.
```tsx {5}
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'dialog',
backdrop: false,
},
]
```
### Effects
Step effects are functions that are called before a step is opened. They are useful for adding custom logic to a step.
This function provides the following methods:
- `next()`: Call this method to move to the next step.
- `show()`: Call this method to show the current step.
- `update(details: StepDetails)`: Call this method to update the details of the current step (say, after data has been
fetched).
```tsx
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'tooltip',
effect({ next, show, update }) {
fetchData().then((res) => {
// update the step details
update({ title: res.title })
// then show show the step
show()
})
return () => {
// cleanup fetch data
}
},
},
]
```
### Wait
Wait steps are useful when you need to wait for a specific condition before proceeding to the next step.
Use the step `effect` function to perform an action and then call `next()` to move to the next step.
> **Note:** You cannot call `show()` in a wait step.
```tsx
const steps: TourStepDetails[] = [
{
id: 'step-1',
type: 'wait',
effect({ next }) {
const button = document.querySelector('#button')
const listener = () => next()
button.addEventListener('click', listener)
return () => button.removeEventListener('click', listener)
},
},
]
```
### Styling
Ensure the `box-sizing` is set to `border-box` for the means of measuring the tour target.
```css
* {
box-sizing: border-box;
}
```
Ensure the `body` has a `position` of `relative`.
```css
body {
position: relative;
}
```
## API Reference
### Props
### Root
#### Props
**`tour`**
Type: `UseTourReturn`
Required: true
Default Value: `undefined`
Description: undefined
**`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.
### ActionTrigger
#### Props
**`action`**
Type: `StepAction`
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.
#### Data Attributes
**`data-scope`**: tour
**`data-part`**: action-trigger
**`data-type`**: The type of the item
**`data-disabled`**: Present when disabled
### Arrow
#### 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.
### ArrowTip
#### 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.
### Backdrop
#### 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`**: tour
**`data-part`**: backdrop
**`data-state`**: "open" | "closed"
**`data-type`**: The type of the item
### CloseTrigger
#### 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`**: tour
**`data-part`**: close-trigger
**`data-type`**: The type of the item
### 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`**: tour
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: popover
**`data-has-nested`**: popover
**`data-type`**: The type of the item
**`data-placement`**: The placement of the content
**`data-side`**: The side of the trigger that the content is positioned on
**`data-step`**:
### 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.
### Description
#### 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`**: tour
**`data-part`**: description
**`data-placement`**: The placement of the description
**`data-side`**: The side of the trigger that the description is positioned on
### 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.
#### Data Attributes
**`data-scope`**: tour
**`data-part`**: positioner
**`data-type`**: The type of the item
**`data-placement`**: The placement of the positioner
**`data-side`**: The side of the trigger that the positioner is positioned on
### ProgressText
#### 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.
### Spotlight
#### 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.
### Title
#### Props
**`asChild`**
Type: `(props: ParentProps<'h2'>) => 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`**: tour
**`data-part`**: title
**`data-placement`**: The placement of the title
**`data-side`**: The side of the trigger that the title is positioned on
### Context
**API:**
| Property | Type | Description |
|----------|------|-------------|
| `open` | `boolean` | Whether the tour is open |
| `totalSteps` | `number` | The total number of steps |
| `stepIndex` | `number` | The index of the current step |
| `step` | `StepDetails | null` | The current step details |
| `hasNextStep` | `boolean` | Whether there is a next step |
| `hasPrevStep` | `boolean` | Whether there is a previous step |
| `firstStep` | `boolean` | Whether the current step is the first step |
| `lastStep` | `boolean` | Whether the current step is the last step |
| `addStep` | `(step: StepDetails) => void` | Add a new step to the tour |
| `removeStep` | `(id: string) => void` | Remove a step from the tour |
| `updateStep` | `(id: string, stepOverrides: Partial) => void` | Update a step in the tour with partial details |
| `setSteps` | `(steps: StepDetails[]) => void` | Set the steps of the tour |
| `setStep` | `(id: string) => void` | Set the current step of the tour |
| `start` | `(id?: string) => void` | Start the tour at a specific step (or the first step if not provided) |
| `isValidStep` | `(id: string) => boolean` | Check if a step is valid |
| `isCurrentStep` | `(id: string) => boolean` | Check if a step is visible |
| `next` | `VoidFunction` | Move to the next step |
| `prev` | `VoidFunction` | Move to the previous step |
| `getProgressText` | `() => string` | Returns the progress text |
| `getProgressPercent` | `() => number` | Returns the progress percent |
# Tree View
## Anatomy
```tsx
```
## Examples
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const Basic = () => {
return (
Tree
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) =>
props.node.children ? (
{nodeState().expanded ? : }
{props.node.name}
{(child, index) => }
) : (
{props.node.name}
)
}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Controlled Expanded
Pass the `expandedValue` and `onExpandedChange` props to the `TreeView.Root` component to control the expanded state of
the tree view.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const ControlledExpanded = () => {
const [expandedValue, setExpandedValue] = createSignal(['node_modules'])
return (
setExpandedValue(expandedValue)}
>
Tree
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) =>
props.node.children ? (
{nodeState().expanded ? : }
{props.node.name}
{(child, index) => }
) : (
{props.node.name}
)
}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Controlled Selection
Pass the `selectedValue` and `onSelectionChange` props to the `TreeView.Root` component to control the selected state of
the tree view.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const ControlledSelected = () => {
const [selectedValue, setSelectedValue] = createSignal(['package.json'])
return (
setSelectedValue(selectedValue)}
>
Tree
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) =>
props.node.children ? (
{nodeState().expanded ? : }
{props.node.name}
{(child, index) => }
) : (
{props.node.name}
)
}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Root Provider
An alternative way to control the tree view is to use the `RootProvider` component and the `useTreeView` hook. This way
you can access the state and methods from outside the component.
```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const RootProvider = () => {
const treeView = useTreeView({ collection })
return (
Tree
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) =>
props.node.children ? (
{nodeState().expanded ? : }
{props.node.name}
{(child, index) => }
) : (
{props.node.name}
)
}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Lazy Loading
Lazy loading is a feature that allows the tree view to load children of a node on demand (or async). This helps to
improve the initial load time and memory usage.
To use this, you need to provide the following:
- `loadChildren` — A function that is used to load the children of a node.
- `onLoadChildrenComplete` — A callback that is called when the children of a node are loaded. Used to update the tree
collection.
- `childrenCount` — A number that indicates the number of children of a branch node.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, FileIcon, FolderIcon, LoaderCircleIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import { useTreeViewNodeContext } from '../use-tree-view-node-context.ts'
// mock api result
const response: Record = {
node_modules: [
{ id: 'zag-js', name: 'zag-js' },
{ id: 'pandacss', name: 'panda' },
{ id: '@types', name: '@types', childrenCount: 2 },
],
'node_modules/@types': [
{ id: 'react', name: 'react' },
{ id: 'react-dom', name: 'react-dom' },
],
src: [
{ id: 'app.tsx', name: 'app.tsx' },
{ id: 'index.ts', name: 'index.ts' },
],
}
// function to load children of a node
function loadChildren(details: TreeView.LoadChildrenDetails): Promise {
const value = details.valuePath.join('/')
return new Promise((resolve) => {
setTimeout(() => {
resolve(response[value] ?? [])
}, 1200)
})
}
export const AsyncLoading = () => {
const [collection, setCollection] = createSignal(initialCollection)
return (
setCollection(e.collection)}
>
Tree
{(node, index) => }
)
}
function TreeNodeIndicator() {
const nodeState = useTreeViewNodeContext()
return nodeState().loading ? :
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
const { node, indexPath } = props
return (
{node.children || node.childrenCount ? (
{node.name}
{(child, index) => }
) : (
{node.name}
)}
)
}
interface Node {
id: string
name: string
children?: Node[]
childrenCount?: number
}
const initialCollection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{ id: 'node_modules', name: 'node_modules', childrenCount: 3 },
{ id: 'src', name: 'src', childrenCount: 2 },
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Lazy Mount
Lazy mounting is a feature that allows the content of a tree view to be rendered only when it is expanded. This is
useful for performance optimization, especially when tree content is large or complex. To enable lazy mounting, use the
`lazyMount` prop on the `TreeView.Root` component.
In addition, the `unmountOnExit` prop can be used in conjunction with `lazyMount` to unmount the tree view content when
branches are collapsed, freeing up resources. The next time a branch is expanded, its content will be re-rendered.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
export const LazyMount = () => {
return (
Tree
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
const { node, indexPath } = props
return (
{node.name}
}
>
{node.name}
{(child, index) => }
)
}
```
### Filtering
Filtering is useful when you have a large tree and you want to filter the nodes to only show the ones that match the
search query. Here's an example that composes the `filter` method from the `TreeCollection` and `useFilter` hook to
filter the nodes.
```tsx
import { useFilter } from '@ark-ui/solid/locale'
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
export const Filtering = () => {
const filterFn = useFilter({ sensitivity: 'base' })
const [collection, setCollection] = createSignal(initialCollection)
const filter = (value: string) => {
const filtered =
value.length > 0 ? initialCollection.filter((node) => filterFn().contains(node.name, value)) : initialCollection
setCollection(filtered)
}
return (
filter(e.currentTarget.value)} />
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
const { node, indexPath } = props
return (
{node.name}
}
>
{node.name}
{(child, index) => }
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const initialCollection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Links
Tree items can be rendered as links to another page or website. This could be useful for documentation sites.
Here's an example that modifies the tree collection to represent an hierarchical link structure. It uses the `asChild`
prop to render the tree items as links, passing the `href` prop to a `` element.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, ExternalLinkIcon, FileIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
export const Links = () => {
return (
Docs
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
const { node, indexPath } = props
return (
}>
{node.name}
}
>
{node.name}
{(child, index) => }
)
}
interface Node {
id: string
name: string
href?: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'docs',
name: 'Documentation',
children: [
{ id: 'docs/getting-started', name: 'Getting Started', href: '/docs/getting-started' },
{ id: 'docs/installation', name: 'Installation', href: '/docs/installation' },
{
id: 'docs/components',
name: 'Components',
children: [
{ id: 'docs/components/accordion', name: 'Accordion', href: '/docs/components/accordion' },
{ id: 'docs/components/dialog', name: 'Dialog', href: '/docs/components/dialog' },
{ id: 'docs/components/menu', name: 'Menu', href: '/docs/components/menu' },
],
},
],
},
{
id: 'examples',
name: 'Examples',
children: [
{ id: 'examples/react', name: 'React Examples', href: '/examples/react' },
{ id: 'examples/vue', name: 'Vue Examples', href: '/examples/vue' },
{ id: 'examples/solid', name: 'Solid Examples', href: '/examples/solid' },
],
},
{
id: 'external',
name: 'External Links',
children: [
{ id: 'external/github', name: 'GitHub Repository', href: 'https://github.com/chakra-ui/zag' },
{ id: 'external/npm', name: 'NPM Package', href: 'https://www.npmjs.com/package/@zag-js/core' },
{ id: 'external/docs', name: 'Official Docs', href: 'https://zagjs.com' },
],
},
{ id: 'readme.md', name: 'README.md', href: '/readme' },
{ id: 'license', name: 'LICENSE', href: '/license' },
],
},
})
```
### Virtualized
For large tree views with thousands of nodes, virtualization can significantly improve performance by only rendering
visible nodes.
Key implementation details:
- Use `useTreeView` hook with `TreeView.RootProvider` for programmatic control
- Pass `scrollToIndexFn` to enable keyboard navigation within the virtualized list
- Use `getVisibleNodes()` to get the flattened list of currently visible nodes
```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/solid/tree-view'
import { createVirtualizer, type Virtualizer } from '@tanstack/solid-virtual'
import { ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'
interface Node {
id: string
name: string
children?: Node[]
}
function generateLargeTree(): Node {
const folders: Node[] = []
for (let i = 0; i < 50; i++) {
const children: Node[] = []
for (let j = 0; j < 20; j++) {
children.push({ id: `folder-${i}/file-${i}-${j}.ts`, name: `file-${i}-${j}.ts` })
}
folders.push({ id: `folder-${i}`, name: `folder-${i}`, children })
}
return {
id: 'ROOT',
name: '',
children: folders,
}
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: generateLargeTree(),
})
const ROW_HEIGHT = 32
export const Virtualized = () => {
let treeRef: HTMLDivElement | undefined
let virtualizerRef: Virtualizer | undefined
const tree = useTreeView({
collection,
scrollToIndexFn(details) {
virtualizerRef?.scrollToIndex(details.index, { align: 'auto' })
},
})
const visibleNodes = () => tree().getVisibleNodes()
const virtualizer = createVirtualizer({
get count() {
return visibleNodes().length
},
getScrollElement: () => treeRef ?? null,
estimateSize: () => ROW_HEIGHT,
overscan: 10,
})
virtualizerRef = virtualizer
return (
Virtualized Tree ({visibleNodes().length} visible nodes)
tree().collapse()}>
Collapse all
tree().expand()}>
Expand all
{(virtualItem) => {
const visibleNode = () => visibleNodes()[virtualItem.index]
const nodeState = () =>
tree().getNodeState({ node: visibleNode().node, indexPath: visibleNode().indexPath })
return (
{
if (e.button !== 0) return
tree().focus(visibleNode().node.id)
}}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
{nodeState().isBranch ? (
{visibleNode().node.name}
) : (
{visibleNode().node.name}
)}
)
}}
)
}
```
### Checkbox Tree
Use the `defaultCheckedValue` prop to enable checkbox selection mode. This allows users to select multiple nodes with
checkboxes, including parent-child selection relationships.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, SquareMinusIcon, SquareIcon } from 'lucide-solid'
import { For } from 'solid-js'
export const CheckboxTree = () => {
return (
Tree
{(node, index) => }
)
}
const TreeNodeCheckbox = () => {
return (
} fallback={ }>
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
const { node, indexPath } = props
return (
{node.children ? (
{node.name}
{(child, index) => }
) : (
{node.name}
)}
)
}
interface Node {
id: string
name: string
children?: Node[] | undefined
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Expand and Collapse All
Use the `expand()` and `collapse()` methods from the tree view context to programmatically expand or collapse all
branches.
```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, Show, createMemo } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'
const ExpandCollapseButtons = () => {
const tree = useTreeViewContext()
const branchValues = createMemo(() => tree().collection.getBranchValues())
const isAllExpanded = createMemo(() => branchValues().every((value) => tree().expandedValue.includes(value)))
return (
tree().expand()}>
Expand all
}
>
tree().collapse()}>
Collapse all
)
}
export const ExpandCollapseAll = () => {
return (
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) => (
{props.node.name}
}
>
}>
{props.node.name}
{(child, index) => }
)}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Mutation
Use the collection's `remove()` and `replace()` methods to dynamically add and remove nodes from the tree. This is
useful for building file explorer interfaces where users can create and delete files.
```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, PlusIcon, TrashIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const Mutation = () => {
const [collection, setCollection] = createSignal(initialCollection)
const removeNode = (props: TreeNodeProps) => {
setCollection((prev) => prev.remove([props.indexPath]))
}
const addNode = (props: TreeNodeProps) => {
const { node, indexPath } = props
const col = collection()
if (!col.isBranchNode(node)) return
const children = [{ id: `untitled-${Date.now()}`, name: 'untitled.tsx' }, ...(node.children || [])]
setCollection(col.replace(indexPath, { ...node, children }))
}
return (
{(node, index) => }
)
}
const TreeNodeActions = (props: TreeNodeProps) => {
const tree = useTreeViewContext()
const isBranch = () => tree().collection.isBranchNode(props.node)
return (
{
e.stopPropagation()
props.onRemove?.(props)
}}
>
{
e.stopPropagation()
props.onAdd?.(props)
tree().expand([props.node.id])
}}
>
)
}
interface TreeNodeProps extends TreeView.NodeProviderProps {
onRemove?: (props: TreeView.NodeProviderProps) => void
onAdd?: (props: TreeView.NodeProviderProps) => void
}
const TreeNode = (props: TreeNodeProps) => {
const tree = useTreeViewContext()
const nodeState = () => tree().getNodeState(props)
return (
{props.node.name}
}
>
{props.node.name}
{(child, index) => (
)}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const initialCollection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
### Rename Node
Enable inline renaming of nodes using the `canRename` prop and `onRenameComplete` callback. Press F2 to
activate rename mode on the focused node.
```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'
export const RenameNode = () => {
const [collection, setCollection] = createSignal(initialCollection)
return (
true}
onRenameComplete={(details) => {
setCollection((prev) => {
const node = prev.at(details.indexPath)
if (!node) return prev
return prev.replace(details.indexPath, { ...node, name: details.label })
})
}}
>
Tree (Press F2 to rename)
{(node, index) => }
)
}
const TreeNode = (props: TreeView.NodeProviderProps) => {
return (
{(nodeState) => (
{props.node.name}}
>
}
>
}>
{props.node.name}
}
>
{(child, index) => }
)}
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const initialCollection = createTreeCollection({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
```
## Guides
### Type Safety
The `TreeView.RootComponent` type enables you to create typed wrapper components that maintain full type safety for tree
nodes.
```tsx
import { TreeView as ArkTreeView } from '@ark-ui/react/tree-view'
const TreeView: ArkTreeView.RootComponent = (props) => {
return {/* ... */}
}
```
Use the wrapper with full type inference on `onSelectionChange` and other callbacks:
```tsx
const App = () => {
const collection = createTreeCollection({
initialItems: [
{ id: '1', label: 'React', children: [] },
{ id: '2', label: 'Vue', children: [] },
],
})
return (
{
// e.items is typed as Array<{ id: string, label: string, children: [] }>
console.log(e.items)
}}
>
{/* ... */}
)
}
```
## API Reference
### Props
### Root
#### Props
**`collection`**
Type: `TreeCollection`
Required: true
Default Value: `undefined`
Description: The collection of tree nodes
**`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.
**`canRename`**
Type: `(node: any, indexPath: IndexPath) => boolean`
Required: false
Default Value: `undefined`
Description: Function to determine if a node can be renamed
**`checkedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled checked node value
**`defaultCheckedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial checked node value when rendered.
Use when you don't need to control the checked node value.
**`defaultExpandedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial expanded node ids when rendered.
Use when you don't need to control the expanded node value.
**`defaultFocusedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial focused node value when rendered.
Use when you don't need to control the focused node value.
**`defaultSelectedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial selected node value when rendered.
Use when you don't need to control the selected node value.
**`expandedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled expanded node ids
**`expandOnClick`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether clicking on a branch should open it or not
**`focusedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The value of the focused node
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>`
Required: false
Default Value: `undefined`
Description: The ids of the tree elements. Useful for composition.
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`loadChildren`**
Type: `(details: LoadChildrenDetails) => Promise`
Required: false
Default Value: `undefined`
Description: Function to load children for a node asynchronously.
When provided, branches will wait for this promise to resolve before expanding.
**`onBeforeRename`**
Type: `(details: RenameCompleteDetails) => boolean`
Required: false
Default Value: `undefined`
Description: Called before a rename is completed. Return false to prevent the rename.
**`onCheckedChange`**
Type: `(details: CheckedChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the checked value changes
**`onExpandedChange`**
Type: `(details: ExpandedChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the tree is opened or closed
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the focused node changes
**`onLoadChildrenComplete`**
Type: `(details: LoadChildrenCompleteDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node finishes loading children
**`onLoadChildrenError`**
Type: `(details: LoadChildrenErrorDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when loading children fails for one or more nodes
**`onRenameComplete`**
Type: `(details: RenameCompleteDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node label rename is completed
**`onRenameStart`**
Type: `(details: RenameStartDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node starts being renamed
**`onSelectionChange`**
Type: `(details: SelectionChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the selection changes
**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to scroll to a specific index.
Useful for virtualized tree views.
**`selectedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled selected node value
**`selectionMode`**
Type: `'multiple' | 'single'`
Required: false
Default Value: `"single"`
Description: Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`typeahead`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the tree supports typeahead search
**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.
### BranchContent
#### 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`**: tree-view
**`data-part`**: branch-content
**`data-state`**: "open" | "closed"
**`data-depth`**: The depth of the item
**`data-path`**: The path of the item
**`data-value`**: The value of the item
### BranchControl
#### 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`**: tree-view
**`data-part`**: branch-control
**`data-path`**: The path of the item
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-selected`**: Present when selected
**`data-focus`**: Present when focused
**`data-renaming`**:
**`data-checked`**: Present when checked
**`data-indeterminate`**:
**`data-value`**: The value of the item
**`data-depth`**: The depth of the item
**`data-loading`**: Present when loading
### BranchIndentGuide
#### 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`**: tree-view
**`data-part`**: branch-indent-guide
**`data-depth`**: The depth of the item
### BranchIndicator
#### 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`**: tree-view
**`data-part`**: branch-indicator
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-selected`**: Present when selected
**`data-focus`**: Present when focused
**`data-loading`**: Present when loading
### Branch
#### 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`**: tree-view
**`data-part`**: branch
**`data-depth`**: The depth of the item
**`data-branch`**:
**`data-value`**: The value of the item
**`data-path`**: The path of the item
**`data-selected`**: Present when selected
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-loading`**: Present when loading
### BranchText
#### 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`**: tree-view
**`data-part`**: branch-text
**`data-disabled`**: Present when disabled
**`data-state`**: "open" | "closed"
**`data-loading`**: Present when loading
### BranchTrigger
#### 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`**: tree-view
**`data-part`**: branch-trigger
**`data-disabled`**: Present when disabled
**`data-state`**: "open" | "closed"
**`data-value`**: The value of the item
**`data-loading`**: Present when loading
### 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`**: tree-view
**`data-part`**: item-indicator
**`data-disabled`**: Present when disabled
**`data-selected`**: Present when selected
**`data-focus`**: Present when focused
### 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.
#### Data Attributes
**`data-scope`**: tree-view
**`data-part`**: item
**`data-path`**: The path of the item
**`data-value`**: The value of the item
**`data-focus`**: Present when focused
**`data-selected`**: Present when selected
**`data-disabled`**: Present when disabled
**`data-renaming`**:
**`data-checked`**: Present when checked
**`data-indeterminate`**:
**`data-depth`**: The depth 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`**: tree-view
**`data-part`**: item-text
**`data-disabled`**: Present when disabled
**`data-selected`**: Present when selected
**`data-focus`**: Present when focused
### Label
#### Props
**`asChild`**
Type: `(props: ParentProps<'h3'>) => Element`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.
### NodeCheckboxIndicator
#### Props
**`fallback`**
Type: `number | boolean | (string & {}) | Node | ArrayElement`
Required: false
Default Value: `undefined`
Description: undefined
**`indeterminate`**
Type: `number | boolean | (string & {}) | Node | ArrayElement`
Required: false
Default Value: `undefined`
Description: undefined
### NodeCheckbox
#### 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`**: tree-view
**`data-part`**: node-checkbox
**`data-state`**: "checked" | "unchecked" | "indeterminate"
**`data-disabled`**: Present when disabled
### NodeProvider
#### Props
**`indexPath`**
Type: `number[]`
Required: true
Default Value: `undefined`
Description: The index path of the tree node
**`node`**
Type: `NonNullable`
Required: false
Default Value: `undefined`
Description: The tree node
### NodeRenameInput
#### 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.
### RootProvider
#### Props
**`value`**
Type: `UseTreeViewReturn`
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.
### Tree
#### 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 |
|----------|------|-------------|
| `collection` | `TreeCollection` | The tree collection data |
| `expandedValue` | `string[]` | The value of the expanded nodes. |
| `setExpandedValue` | `(value: string[]) => void` | Sets the expanded value |
| `selectedValue` | `string[]` | The value of the selected nodes. |
| `setSelectedValue` | `(value: string[]) => void` | Sets the selected value |
| `checkedValue` | `string[]` | The value of the checked nodes |
| `toggleChecked` | `(value: string, isBranch: boolean) => void` | Toggles the checked value of a node |
| `setChecked` | `(value: string[]) => void` | Sets the checked value of a node |
| `clearChecked` | `VoidFunction` | Clears the checked value of a node |
| `getCheckedMap` | `() => CheckedValueMap` | Returns the checked details of branch and leaf nodes |
| `getVisibleNodes` | `() => VisibleNode[]` | Returns the visible nodes as a flat array of nodes and their index path.
Useful for rendering virtualized tree views. |
| `expand` | `(value?: string[]) => void` | Function to expand nodes.
If no value is provided, all nodes will be expanded |
| `collapse` | `(value?: string[]) => void` | Function to collapse nodes
If no value is provided, all nodes will be collapsed |
| `select` | `(value?: string[]) => void` | Function to select nodes
If no value is provided, all nodes will be selected |
| `deselect` | `(value?: string[]) => void` | Function to deselect nodes
If no value is provided, all nodes will be deselected |
| `focus` | `(value: string) => void` | Function to focus a node by value |
| `selectParent` | `(value: string) => void` | Function to select the parent node of the focused node |
| `expandParent` | `(value: string) => void` | Function to expand the parent node of the focused node |
| `startRenaming` | `(value: string) => void` | Function to start renaming a node by value |
| `submitRenaming` | `(value: string, label: string) => void` | Function to submit the rename and update the node label |
| `cancelRenaming` | `() => void` | Function to cancel renaming without changes |
## Accessibility
Complies with the [Tree View WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).
### Keyboard Support
**`Tab`**
Description: Moves focus to the tree view, placing the first tree view item in focus.
**`Enter + Space`**
Description: Selects the item or branch node
**`ArrowDown`**
Description: Moves focus to the next node
**`ArrowUp`**
Description: Moves focus to the previous node
**`ArrowRight`**
Description: When focus is on a closed branch node, opens the branch. When focus is on an open branch node, moves focus to the first item node.
**`ArrowLeft`**
Description: When focus is on an open branch node, closes the node. When focus is on an item or branch node, moves focus to its parent branch node.
**`Home`**
Description: Moves focus to first node without opening or closing a node.
**`End`**
Description: Moves focus to the last node that can be focused without expanding any nodes that are closed.
**`a-z + A-Z`**
Description: Focus moves to the next node with a name that starts with the typed character. The search logic ignores nodes that are descendants of closed branch.
**`*`**
Description: Expands all sibling nodes that are at the same depth as the focused node.
**`Shift + ArrowDown`**
Description: Moves focus to and toggles the selection state of the next node.
**`Shift + ArrowUp`**
Description: Moves focus to and toggles the selection state of the previous node.
**`Ctrl + A`**
Description: Selects all nodes in the tree. If all nodes are selected, unselects all nodes.
# UTILITIES
---
# Client Only
## Motivation
The `ClientOnly` component renders its children only on the client side. This is useful for components that need to
access the DOM or browser APIs that are not available on the server side.
## Examples
### Basic
```tsx
import { ClientOnly } from '@ark-ui/solid/client-only'
export const Basic = () => (
This content is only rendered on the client side.
)
```
### With Fallback
```tsx
import { ClientOnly } from '@ark-ui/solid/client-only'
export const WithFallback = () => (
Loading... }>
This content is only rendered on the client side.
)
```
## API Reference
### ClientOnly
#### Props
**`fallback`**
Type: `number | boolean | Node | ArrayElement | (string & {})`
Required: false
Default Value: `undefined`
Description: undefined
# Download Trigger
## Motivation
The `DownloadTrigger` component provides a convenient way to programmatically trigger file downloads in web
applications. It handles the complexities of downloading files, whether they are URLs, Blobs, or other data types.
## Examples
### Basic
Pass the data you want to download to the `data` prop, and specify the `fileName` and `mimeType` of the file.
```tsx
import { DownloadTrigger } from '@ark-ui/solid/download-trigger'
import { DownloadIcon, FileIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/download-trigger.module.css'
const content = 'Hello, World! This is a sample text file.'
export const Basic = () => {
return (
)`
Required: true
Default Value: `undefined`
Description: The data to download
**`fileName`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The name of the file to download
**`mimeType`**
Type: `FileMimeType`
Required: true
Default Value: `undefined`
Description: The MIME type of the data to download
**`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.
# Environment
## Motivation
We use [Zag.js](https://zagjs.com/overview/composition#custom-window-environment) internally, which relies on DOM query
methods like `document.querySelectorAll` and `document.getElementById`. In custom environments like iframes, Shadow DOM,
or Electron, these methods might not work as expected.
To handle this, Ark UI includes the `EnvironmentProvider`, allowing you to set the appropriate root node or document,
ensuring correct DOM queries.
## Setup
To support custom environments like an iframe, Shadow DOM or Electron, render the `EnvironmentProvider` component to
provide the environment context to all Ark UI components.
```tsx
import { EnvironmentProvider } from '@ark-ui/solid/environment'
export const App = () => {
return (
)
}
```
### Usage in iframe
The `EnvironmentProvider` component will automatically detect the current environment and set the correct environment
context. However, you can also manually set the `Document` like shown in this React example below:
```jsx
import Frame, { FrameContextConsumer } from 'react-frame-component'
import { EnvironmentProvider } from '@ark-ui/react'
export const App = () => (
{({ document }) => {/* Your App */} }
)
```
### Usage in Shadow DOM
Here's an example of how to set the `EnvironmentProvider`'s value with Shadow DOM in Solid.js `Portal` component.
```jsx
import { EnvironmentProvider } from '@ark-ui/react'
import { Index, Portal } from 'solid-js/web'
export const App = () => {
let portalNode
return (
portalNode?.shadowRoot ?? document}>{/* Your App */}
)
}
```
## Context
Use the `useEnvironmentContext` hook to access the `RootNode`, `Document`, and `Window`.
```tsx
import { useEnvironmentContext } from '../use-environment-context.ts'
export const Usage = () => {
const environment = useEnvironmentContext()
return {JSON.stringify(environment().getRootNode(), null, 2)}
}
```
## API Reference
### EnvironmentProvider
#### Props
**`value`**
Type: `RootNode | (() => RootNode)`
Required: false
Default Value: `undefined`
Description: undefined
# Focus Trap
## Motivation
Focus trapping is essential for modal interfaces and other interactive elements that require user attention.
The `FocusTrap` component helps maintain accessibility by ensuring keyboard focus remains within a designated container
until explicitly released.
## Examples
```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { createSignal } from 'solid-js'
export const Basic = () => {
const [trapped, setTrapped] = createSignal(false)
return (
<>
setTrapped(true)}>Start Trap
setTrapped(false)}>End Trap
>
)
}
```
### Autofocus
The focus trap respects elements with the `autofocus` attribute.
```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { Show, createSignal } from 'solid-js'
export const Autofocus = () => {
const [trapped, setTrapped] = createSignal(false)
let buttonRef!: HTMLButtonElement
return (
)
}
```
### Initial Focus
Use the `initialFocus` prop to set the element that should receive initial focus when the trap is activated.
```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { createSignal } from 'solid-js'
export const InitialFocus = () => {
const [trapped, setTrapped] = createSignal(false)
const toggle = () => setTrapped((v) => !v)
let inputRef!: HTMLInputElement
return (
)
}
```
## API Reference
### FocusTrap
#### 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.
**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the focus trap is disabled.
**`fallbackFocus`**
Type: `FocusTarget`
Required: false
Default Value: `undefined`
Description: By default, an error will be thrown if the focus trap contains no
elements in its tab order. With this option you can specify a
fallback element to programmatically receive focus if no other
tabbable elements are found. For example, you may want a popover's
`` to receive focus if the popover's content includes no
tabbable elements. *Make sure the fallback element has a negative
`tabindex` so it can be programmatically focused.
NOTE: If `initialFocus` is `false` (or a function that returns `false`),
this function will not be called when the trap is activated, and no element
will be initially focused. This function may still be called while the trap
is active if things change such that there are no longer any tabbable nodes
in the trap.
**`initialFocus`**
Type: `VoidFunction | FocusTargetOrFalse`
Required: false
Default Value: `undefined`
Description: By default, when a focus trap is activated the first element in the
focus trap's tab order will receive focus. With this option you can
specify a different element to receive that initial focus, or use `false`
for no initially focused element at all.
NOTE: Setting this option to `false` (or a function that returns `false`)
will prevent the `fallbackFocus` option from being used.
Setting this option to `undefined` (or a function that returns `undefined`)
will result in the default behavior.
**`onActivate`**
Type: `VoidFunction`
Required: false
Default Value: `undefined`
Description: A function that will be called **before** sending focus to the
target element upon activation.
**`onDeactivate`**
Type: `VoidFunction`
Required: false
Default Value: `undefined`
Description: A function that will be called **before** sending focus to the
trigger element upon deactivation.
**`persistentElements`**
Type: `(() => Element | null)[]`
Required: false
Default Value: `undefined`
Description: Additional elements to treat as part of the trap, for portalled content
not discoverable via `aria-controls`/`aria-expanded` (see `followControlledElements`).
**`returnFocusOnDeactivate`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Default: `true`. If `false`, when the trap is deactivated,
focus will *not* return to the element that had focus before activation.
**`setReturnFocus`**
Type: `type ONLY_FOR_FORMAT =
FocusTargetValueOrFalse | ((nodeFocusedBeforeActivation: HTMLElement | SVGElement) => FocusTargetValueOrFalse)`
Required: false
Default Value: `undefined`
Description: By default, focus trap on deactivation will return to the element
that was focused before activation.
# Format Byte
## Usage
The byte formatting component extends the number formatting capabilities to handle byte-specific formatting, including
automatic unit conversion and display options.
```jsx
import { Format } from '@ark-ui/react'
```
## Examples
### Basic
Use the `Format.Byte` component to format a byte value with default options.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const ByteBasic = () => {
return (
File size
)
}
```
### Sizes
Use the `sizes` prop to specify custom byte sizes for formatting.
```tsx
import { Format } from '@ark-ui/solid/format'
import { For } from 'solid-js'
import styles from 'styles/format.module.css'
export const ByteSizes = () => {
const byteSizes = [50, 5000, 5000000, 5000000000]
return (
)
}
```
### Locale
Use the `locale` prop to format the byte value according to a specific locale.
```tsx
import { Format } from '@ark-ui/solid/format'
import { LocaleProvider } from '@ark-ui/solid/locale'
import { For } from 'solid-js'
import styles from 'styles/format.module.css'
export const ByteWithLocale = () => {
const locales = ['de-DE', 'zh-CN']
return (
{(locale) => (
{locale}:
)}
)
}
```
### Unit
Use the `unit` prop to specify the unit of the byte value.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const ByteWithUnit = () => {
return (
File size:
)
}
```
### Unit Display
Use the `unitDisplay` prop to specify the display of the unit.
```tsx
import { Format } from '@ark-ui/solid/format'
import { For } from 'solid-js'
import styles from 'styles/format.module.css'
export const ByteWithUnitDisplay = () => {
const unitDisplays = ['narrow', 'short', 'long'] as const
return (
{(unitDisplay) => (
{unitDisplay}:
)}
)
}
```
### Unit System
Use the `unitSystem` prop to specify whether to use decimal (1000 bytes) or binary (1024 bytes) unit system.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const ByteWithUnitSystem = () => {
return (
Decimal (1000 bytes):
Binary (1024 bytes):
)
}
```
# Format Number
## Usage
The number formatting logic is handled by the native `Intl.NumberFormat` API and smartly cached to avoid performance
issues when using the same locale and options.
```jsx
import { Format } from '@ark-ui/react'
```
## Examples
### Basic
Use the `Format.Number` component to format a number with default options.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const NumberBasic = () => {
return (
)
}
```
### Percentage
Use the `style="percent"` prop to format the number as a percentage.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const NumberWithPercentage = () => {
return (
)
}
```
### Currency
Use the `style="currency"` prop along with the `currency` prop to format the number as a currency.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const NumberWithCurrency = () => {
return (
)
}
```
### Locale
Use the `locale` prop to format the number according to a specific locale.
```tsx
import { Format } from '@ark-ui/solid/format'
import { LocaleProvider } from '@ark-ui/solid/locale'
import styles from 'styles/format.module.css'
export const NumberWithLocale = () => {
return (
)
}
```
### Unit
Use the `style="unit"` prop along with the `unit` prop to format the number with a specific unit.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const NumberWithUnit = () => {
return (
)
}
```
### Compact Notation
Use the `notation="compact"` prop to format the number in compact notation.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const NumberWithCompact = () => {
return (
downloads per month
)
}
```
# Format Time
## Usage
The time formatting logic supports both string (`HH:mm[:ss]`) and `Date` inputs.
```jsx
import { Format } from '@ark-ui/react'
```
## Examples
### Basic
Use the `Format.Time` component to format a time value with default options.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const TimeBasic = () => {
return (
)
}
```
### Date
Use a `Date` object as the `value`.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const TimeWithDate = () => {
return (
Boarding
)
}
```
### Seconds
Use the `withSeconds` prop to include seconds in the output.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const TimeWithSeconds = () => {
return (
Last sync
)
}
```
### AM/PM Labels
Use `amLabel` and `pmLabel` to customize day-period labels when using 12-hour format.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const TimeWithAmPmLabels = () => {
return (
Support window
)
}
```
### Locale
Use the locale provider to format the time according to a specific locale.
```tsx
import { Format } from '@ark-ui/solid/format'
import { LocaleProvider } from '@ark-ui/solid/locale'
import styles from 'styles/format.module.css'
export const TimeWithLocale = () => {
return (
)
}
```
# Format Relative Time
## Usage
The relative time formatting logic is handled by the native `Intl.RelativeTimeFormat` API and smartly cached to avoid
performance issues when using the same locale and options.
```jsx
import { Format } from '@ark-ui/react'
```
## Examples
### Basic
Use the `Format.RelativeTime` component to format a relative time with default options.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const RelativeTimeBasic = () => {
return (
Last updated
)
}
```
### Short
Use the `style="short"` prop to format the relative time in short format.
```tsx
import { Format } from '@ark-ui/solid/format'
import styles from 'styles/format.module.css'
export const RelativeTimeShort = () => {
return (
Edited
)
}
```
# Frame
## Usage
The `Frame` component is used to render a component in an iframe.
- Tracks the size of the content and exposes them via css variables.
- Support for `head` prop to inject scripts and styles.
- Support for mount and unmount callbacks.
```jsx
import { Frame } from '@ark-ui/react'
```
## Examples
### Basic
Wrap your component in the `Frame` component to render it in an iframe.
```tsx
import { Frame } from '@ark-ui/solid/frame'
export const Basic = () => {
return (
{'body { background-color: #f0f0f0; }'}}
>
Hello from inside the frame!
This content is rendered within our custom frame component using a Portal.
)
}
```
### Injecting Script
Using the `onMount` prop, you can inject a script into the iframe.
```tsx
import { Frame } from '@ark-ui/solid/frame'
export const Script = () => {
let ref: HTMLIFrameElement | undefined
return (
{
const doc = ref?.contentDocument
if (!doc) return
const script = doc.createElement('script')
script.innerHTML = 'console.log("Hello from inside the frame!")'
doc.body.appendChild(script)
}}
style={{ border: '1px solid #ccc', width: '100%', height: 'var(--height)' }}
>
Hello from inside the frame!
This content is rendered within our custom frame component using a Portal.
)
}
```
### Custom src doc
Use the `srcDoc` prop to specify the HTML content of the page to use in the iframe.
```tsx
import { Frame } from '@ark-ui/solid/frame'
const srcDoc = `
`
export const SrcDoc = () => {
return (
Hello from inside the frame!
This content is rendered within our custom frame component using a Portal.
The frame has custom initial content, including Font Awesome and Open Sans font.
)
}
```
## API Reference
### Props
### Frame
#### Props
**`head`**
Type: `number | boolean | Node | ArrayElement | (string & {})`
Required: false
Default Value: `undefined`
Description: Additional content to be inserted into the frame's
**`onMount`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Callback function to be executed when the frame is mounted
**`onUnmount`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Callback function to be executed when the frame is unmounted
# Highlight
## Usage
The Highlight component takes a `text` prop containing the full text and a `query` prop specifying the text to
highlight. It then renders the text with highlighted portions wrapped in `
` tags.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import styles from 'styles/highlight.module.css'
export const Basic = () => (
)
```
### Dynamic Query
Control the `query` prop with state to create an interactive search highlighting experience.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import { createSignal } from 'solid-js'
import field from 'styles/field.module.css'
import styles from 'styles/highlight.module.css'
export const DynamicQuery = () => {
const [query, setQuery] = createSignal('component')
return (
)
}
```
### Multiple Queries
You can highlight multiple terms by passing an array of strings to the `query` prop.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import styles from 'styles/highlight.module.css'
export const Multiple = () => (
)
```
### Case Sensitivity
By default, the highlighting is case-sensitive. Use the `ignoreCase` prop to make it case-insensitive.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import styles from 'styles/highlight.module.css'
export const IgnoreCase = () => (
)
```
### Match All
By default, the Highlight component matches the first occurrence of the query. To highlight all occurrences of the
query, set the `matchAll` prop to `true`.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import styles from 'styles/highlight.module.css'
export const MatchAll = () => (
)
```
### Exact Match
By default, the Highlight component matches partial words. Use the `exactMatch` prop to only highlight whole words that
match the query exactly.
```tsx
import { Highlight } from '@ark-ui/solid/highlight'
import styles from 'styles/highlight.module.css'
export const ExactMatch = () => (
)
```
## API Reference
### Highlight
#### Props
**`query`**
Type: `string | string[]`
Required: true
Default Value: `undefined`
Description: The query to highlight in the text
**`text`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The text to highlight
**`exactMatch`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to match whole words only
**`ignoreCase`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to ignore case while matching
**`matchAll`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to match multiple instances of the query
## Customization
The Highlight component wraps matched text in `` tags. Pass a `className` (or `class` in Solid/Svelte/Vue) to
style the highlighted portions.
```tsx
```
Style the `mark` tags using CSS to customize the appearance of highlighted text.
```css
.highlight-mark {
background-color: #ffe5e4;
color: #c9453b;
border-radius: 0.125rem;
}
```
# Hotkeys
## Setup
Register a shortcut with `useHotkey`. It listens on the document and cleans up on unmount.
```tsx
import { useFormatHotkey, useHotkey } from '@ark-ui/solid/hotkeys'
import { createSignal } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
export const Basic = () => {
const [count, setCount] = createSignal(0)
const formatHotkey = useFormatHotkey()
useHotkey({ hotkey: 'mod+K', action: () => setCount((value) => value + 1) })
return (
Press {formatHotkey('mod+K')} anywhere on this page
{count()}
{count() === 1 ? 'time' : 'times'}
)
}
```
`mod` resolves to Command on macOS and Control elsewhere, so you write the binding once.
## Examples
### Multiple shortcuts
`useHotkeys` takes an array, which is what you want when the list comes from data. Each command needs a `hotkey` and an
`action`.
```tsx
import { useFormatHotkey, useHotkeys, usePlatform } from '@ark-ui/solid/hotkeys'
import { For, createSignal } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
const commands = [
{ id: 'save', hotkey: 'mod+S', label: 'Save', category: 'File' },
{ id: 'undo', hotkey: 'mod+Z', label: 'Undo', category: 'Edit' },
{ id: 'redo', hotkey: 'mod+shift+Z', label: 'Redo', category: 'Edit' },
]
export const Multiple = () => {
const [lastFired, setLastFired] = createSignal(null)
const platform = usePlatform()
const formatHotkey = useFormatHotkey()
useHotkeys({ commands: commands.map((command) => ({ ...command, action: () => setLastFired(command.id) })) })
return (
Detected platform
{platform()}
{(command) => (
{command.label}
{formatHotkey(command.hotkey)}
)}
)
}
```
### Sequences
Press one key, then another. `G > H` fires only if both land inside the sequence window.
```tsx
import { useHotkeys } from '@ark-ui/solid/hotkeys'
import { For, createSignal } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
const routes = [
{ id: 'home', hotkey: 'G > H', keys: ['G', 'H'], label: 'Home' },
{ id: 'settings', hotkey: 'G > S', keys: ['G', 'S'], label: 'Settings' },
]
export const Sequence = () => {
const [page, setPage] = createSignal('home')
useHotkeys({
commands: routes.map((route) => ({ id: route.id, hotkey: route.hotkey, action: () => setPage(route.id) })),
})
return (
Press the keys in order, one after the other
{(route) => (
{route.label}
{(key, index) => (
{index() > 0 ? `then ${key}` : key}
)}
)}
Current page
{page()}
)
}
```
### Sequence timeout
`sequenceTimeoutMs` sets that window, which defaults to one second. Wait longer and the sequence resets without firing.
```tsx
import { createHotkeyStore, useHotkey } from '@ark-ui/solid/hotkeys'
import { createSignal } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
const TIMEOUT_MS = 600
const store = createHotkeyStore({ sequenceTimeoutMs: TIMEOUT_MS })
export const SequenceTimeout = () => {
const [completed, setCompleted] = createSignal(0)
useHotkey({ hotkey: 'G > H', action: () => setCompleted((value) => value + 1), store })
return (
Press G then H . The second key must land within{' '}
{TIMEOUT_MS}ms, otherwise the sequence resets and nothing fires.
{completed()}
{completed() === 1 ? 'completion' : 'completions'}
sequenceTimeoutMs
{TIMEOUT_MS}ms
)
}
```
### Scopes
A command with `scopes: ['editor']` only fires while that scope is active. `store.setScope` swaps an entire set of
shortcuts at once.
```tsx
import { createHotkeyStore, useFormatHotkey, useHotkeys } from '@ark-ui/solid/hotkeys'
import { For, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/hotkeys.module.css'
const commands = [
{ id: 'bold', hotkey: 'mod+B', label: 'Bold', scope: 'editor' },
{ id: 'print', hotkey: 'mod+P', label: 'Print', scope: 'reader' },
]
const store = createHotkeyStore({ activeScopes: ['editor'] })
export const Scopes = () => {
const formatHotkey = useFormatHotkey()
const [scope, setScope] = createSignal('editor')
const [fired, setFired] = createSignal(null)
useHotkeys({
commands: commands.map((command) => ({
id: command.id,
hotkey: command.hotkey,
scopes: [command.scope],
action: () => setFired(command.id),
})),
store,
})
const toggle = () => {
const next = scope() === 'editor' ? 'reader' : 'editor'
setScope(next)
setFired(null)
store.setScope(next)
}
return (
Only commands in the active scope respond
Switch scope
{scope()}
{(command) => (
{command.label} · {command.scope}
{formatHotkey(command.hotkey)}
)}
)
}
```
### Form fields
Single keys are ignored while you type in an input, textarea or select. Shortcuts with a modifier still fire, because
`Cmd+S` in a text field still means save. Opt a single key back in with `options: { enableOnFormTags: true }`.
```tsx
import { useFormatHotkey, useHotkeys } from '@ark-ui/solid/hotkeys'
import { createSignal } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
export const FormFields = () => {
const [log, setLog] = createSignal(null)
const formatHotkey = useFormatHotkey()
useHotkeys({
commands: [
{ hotkey: 'S', action: () => setLog('Search (single key)') },
{ hotkey: 'mod+S', action: () => setLog('Save (modifier)') },
{
hotkey: 'P',
action: () => setLog('Preview (opted in)'),
options: { enableOnFormTags: true },
},
],
})
return (
)
}
```
### Conflicts
When two commands claim the same shortcut, `conflictBehavior` decides. `warn` is the default and keeps both, `replace`
drops the earlier one, `allow` keeps both silently, `error` refuses the second.
```tsx
import {
type ConflictBehavior,
type HotkeyStore,
createHotkeyStore,
useHotkeyRegistrations,
useHotkeys,
} from '@ark-ui/solid/hotkeys'
import { For, Show, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/hotkeys.module.css'
const BEHAVIORS: ConflictBehavior[] = ['warn', 'replace', 'allow']
const Demo = (props: { store: HotkeyStore }) => {
const [fired, setFired] = createSignal([])
useHotkeys({
commands: [
{ id: 'first', hotkey: 'mod+K', action: () => setFired((log) => [...log, 'First']), label: 'First' },
{ id: 'second', hotkey: 'mod+K', action: () => setFired((log) => [...log, 'Second']), label: 'Second' },
],
store: props.store,
})
const commands = useHotkeyRegistrations({ store: props.store })
return (
<>
Registered on mod+K
0} fallback={none }>
{(command) => {command.label} }
Fired on last press
0 ? 'active' : undefined}>
{fired().slice(-2).join(' + ') || 'nothing yet'}
>
)
}
export const Conflicts = () => {
const [behavior, setBehavior] = createSignal('warn')
return (
Two commands claim the same shortcut. Pick how the store resolves it, then press it.
{(value) => (
setBehavior(value)}>
{value}
)}
{(current) => {
// Built once per behavior: Solid props are getters, so an inline call would
// rebuild the store on every read.
const store = createHotkeyStore({ conflictBehavior: current })
return
}}
)
}
```
### Key state
`usePressedKeys` returns the keys currently held, `useIsKeyPressed` answers for one. Use these when a held key changes
what an interaction means, like Shift to extend a selection.
```tsx
import { useHotkey, useIsKeyPressed, usePressedKeys } from '@ark-ui/solid/hotkeys'
import { For, Show } from 'solid-js'
import styles from 'styles/hotkeys.module.css'
export const KeyState = () => {
useHotkey({ hotkey: 'mod+K', action: () => {} })
const pressedKeys = usePressedKeys()
const isShiftPressed = useIsKeyPressed({ hotkey: 'shift' })
return (
Hold any key to see it tracked live
Currently pressed
0} fallback={nothing }>
{(key) => (
{key}
)}
Shift
{isShiftPressed() ? 'Precision mode' : 'Hold Shift for precision'}
)
}
```
### Recording a shortcut
`useHotkeyRecorder` captures whatever the user presses, for a "click to rebind" setting. Escape cancels, Backspace
clears, and chords and sequences both work.
```tsx
import { useHotkeyRecorder } from '@ark-ui/solid/hotkeys'
import { Show, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/hotkeys.module.css'
export const Recorder = () => {
const [binding, setBinding] = createSignal(null)
const [lastEvent, setLastEvent] = createSignal(null)
const recorder = useHotkeyRecorder({
onRecord: (hotkey) => {
setBinding(hotkey.display)
setLastEvent('recorded')
},
onCancel: () => setLastEvent('cancelled'),
onClear: () => {
setBinding(null)
setLastEvent('cleared')
},
})
return (
Click record, then press a shortcut. Esc cancels,{' '}
Backspace clears.
recorder.start()}
disabled={recorder.state().recording}
>
{recorder.state().recording ? 'Listening…' : 'Record shortcut'}
{recorder.state().value?.display ?? 'Press a key'}
Bound to
nothing yet}>
{binding()}
Last event
{lastEvent() ?? 'none'}
)
}
```
### Command palette
`useHotkeyRegistrations` returns every registered command with its metadata, so the palette is a view over the registry
instead of a second list you keep in sync.
```tsx
import { Combobox, useListCollection } from '@ark-ui/solid/combobox'
import { Dialog } from '@ark-ui/solid/dialog'
import {
createHotkeyStore,
useFormatHotkey,
useHotkey,
useHotkeyRegistrations,
useHotkeys,
} from '@ark-ui/solid/hotkeys'
import { useFilter } from '@ark-ui/solid/locale'
import { CornerDownLeftIcon, SearchIcon } from 'lucide-solid'
import { For, Show, createEffect, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/command-palette.module.css'
// Its own store, so the palette lists only the commands registered here.
const store = createHotkeyStore()
export const CommandPalette = () => {
const [open, setOpen] = createSignal(false)
const [lastRun, setLastRun] = createSignal(null)
useHotkeys({
commands: [
{
id: 'save',
hotkey: 'mod+S',
action: () => setLastRun('Save file'),
label: 'Save file',
category: 'File',
keywords: ['write', 'persist'],
},
{
id: 'theme',
hotkey: 'mod+shift+D',
action: () => setLastRun('Toggle theme'),
label: 'Toggle theme',
category: 'View',
keywords: ['dark', 'light', 'appearance'],
},
{
id: 'undo',
hotkey: 'mod+Z',
action: () => setLastRun('Undo'),
label: 'Undo',
category: 'Edit',
keywords: ['revert', 'back'],
},
],
store,
})
const formatHotkey = useFormatHotkey()
const commands = useHotkeyRegistrations({ store })
const filterFn = useFilter({ sensitivity: 'base' })
const { collection, filter, set } = useListCollection({
initialItems: commands(),
itemToString: (item) => item.label ?? item.id,
itemToValue: (item) => item.id,
groupBy: (item) => item.category ?? 'Other',
filter: (itemText, filterText, item) =>
filterFn().contains(itemText, filterText) ||
item.keywords.some((keyword) => filterFn().contains(keyword, filterText)),
})
createEffect(() => {
set([...commands()])
})
const openPalette = () => setOpen(true)
useHotkey({
hotkey: 'mod+shift+P',
action: openPalette,
label: 'Open command palette',
category: 'General',
store,
})
const handleValueChange = (details: Combobox.ValueChangeDetails) => {
const selected = details.items.at(0)
if (!selected) return
setOpen(false)
selected.action(new KeyboardEvent('keydown'))
}
return (
Open palette ({formatHotkey('mod+shift+P')})
Last run: {lastRun() ?? 'nothing yet'}
setOpen(details.open)}
onExitComplete={() => filter('')}
>
filter(details.inputValue)}
onValueChange={handleValueChange}
>
0} fallback={No commands found
}>
{([category, group]) => (
{category}
{(item) => (
{item.label ?? item.id}
{formatHotkey(item.hotkey)}
)}
)}
)
}
```
Register once with `label`, `category` and `keywords`, then group by `category`, search `keywords`, and call
`item.action` on select. Here, typing "dark" finds "Toggle theme" even though its label has no "dark" in it.
## Guides
### Displaying a shortcut
`useFormatHotkey` returns a formatter bound to the current platform, so `mod+K` renders as `⌘ K` on macOS and `Ctrl K`
elsewhere.
```tsx
const formatHotkey = useFormatHotkey()
return {formatHotkey('mod+K')}
```
Use `formatHotkey` from `@zag-js/hotkeys` only outside a component. Inside one it reads the platform during render,
which mismatches on hydration when the server says `Ctrl K` and the browser says `⌘ K`. For the platform itself,
`usePlatform` returns `mac`, `windows` or `linux`.
### Using your own store
Without one, every hook registers on a shared default store. Create your own with `createHotkeyStore` and pass it to any
hook to isolate a set of commands, set defaults for all of them, or control active scopes.
```tsx
const store = createHotkeyStore({
activeScopes: ['editor'],
conflictBehavior: 'replace',
sequenceTimeoutMs: 800,
})
useHotkeys({ commands, store })
useHotkey({ hotkey: 'mod+S', action: save, store })
```
Build the store outside the component, or memoize it. One created during render is rebuilt on every render and loses its
registrations.
A command palette is the usual reason to reach for this: give it its own store and `useHotkeyRegistrations({ store })`
returns only the commands registered on it, instead of everything on the page.
### Enabling and disabling
Pass `enabled` as a boolean or a function. A function is re-evaluated each time the key fires, so it reads current state
without re-registering.
```tsx
useHotkey({ hotkey: 'mod+S', action: save, enabled: () => !isReadOnly })
```
### Reacting to a key release
`options: { eventType: 'keyup' }` fires on release instead of press. Pair it with a `keydown` command on the same key
for push-to-talk.
## API Reference
### createHotkeyStore
#### Props
**`activeScopes`**
Type: `string | string[]`
Required: false
Default Value: `['*']`
Description: The scopes that start active. Only commands in an active scope fire.
**`conflictBehavior`**
Type: `'warn' | 'error' | 'replace' | 'allow'`
Required: false
Default Value: `'warn'`
Description: What to do when two commands register the same hotkey. warn keeps both and logs, replace drops the earlier one, allow keeps both silently, error refuses the second.
**`defaultOptions`**
Type: `HotkeyOptions`
Required: false
Default Value: `undefined`
Description: Options applied to every command registered on this store. Per-command options override it.
**`sequenceTimeoutMs`**
Type: `number`
Required: false
Default Value: `1000`
Description: How long a sequence like G > H waits for the next key before resetting.
**`returns`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store. Pass it to any hook as store, and keep it stable: build it outside the component or memoize it, since one created during render is rebuilt on every render and loses its registrations.
### useHotkey
#### Props
**`props`**
Type: `MaybeAccessor`
Required: true
Default Value: `undefined`
Description: One command, plus an optional store. Takes every field of UseHotkeysCommand.
### useHotkeys
#### Props
**`commands`**
Type: `UseHotkeysCommand[]`
Required: true
Default Value: `undefined`
Description: The commands to register. Passed inside a single object, so the whole argument is MaybeAccessor<{ commands, store, id }>.
**`store`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store to register on. Defaults to a store shared by every hook that does not name one.
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Prefix for the ids generated for commands that do not set their own. One is generated when omitted.
### UseHotkeysCommand
#### Props
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Identifies the command across renders. One is generated when omitted, keyed by position within the hook instance, which is enough unless something else needs to address the command by name.
**`hotkey`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The key combination or sequence that triggers the command.
**`action`**
Type: `(event: KeyboardEvent) => void`
Required: true
Default Value: `undefined`
Description: Called when the hotkey fires.
**`label`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Human-readable name. Read back by useHotkeyRegistrations, so a command palette can render it.
**`description`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Longer explanation of what the command does.
**`category`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Group name, for sectioning a command palette.
**`keywords`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: Alternative search terms. Lets a palette match "dark" against a command labelled "Toggle theme".
**`scopes`**
Type: `string | string[]`
Required: false
Default Value: `'*'`
Description: The scopes this command belongs to. It only fires while one of them is active.
**`enabled`**
Type: `boolean | (() => boolean)`
Required: false
Default Value: `true`
Description: Whether the command can fire. A function is re-evaluated on every key press, so it reads current state without re-registering.
**`options`**
Type: `HotkeyOptions`
Required: false
Default Value: `undefined`
Description: Per-command behavior. Overrides the provider defaultOptions.
### HotkeyOptions
#### Props
**`preventDefault`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Call preventDefault() on the event before running the action.
**`stopPropagation`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Call stopPropagation() on the event before running the action.
**`enableOnFormTags`**
Type: `boolean | ('input' | 'textarea' | 'select')[]`
Required: false
Default Value: `false`
Description: Whether a single-key shortcut fires while an input, textarea or select has focus. Shortcuts with a modifier always fire. Pass an array to opt in to specific tags.
**`enableOnContentEditable`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether the shortcut fires inside a contenteditable element.
**`capture`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Listen in the capture phase.
**`requireReset`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Fire once per press. The key must be released before it fires again, which suppresses key repeat.
**`eventType`**
Type: `'keydown' | 'keyup'`
Required: false
Default Value: `'keydown'`
Description: Whether to fire on press or on release. Pair a keyup command with a keydown one on the same key for push-to-talk.
**`target`**
Type: `Element | (() => Element | null)`
Required: false
Default Value: `undefined`
Description: Scope the command to a DOM subtree. It only fires when the event originates inside this element, which must contain focus. Resolved on every event, and skipped while it resolves to null.
### useHotkeyRegistrations
#### Props
**`store`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store to read from. Defaults to a store shared by every hook that does not name one.
**`returns`**
Type: `Accessor`
Required: false
Default Value: `undefined`
Description: Every command currently registered on the store, with its label, category, keywords and resolved hotkey. Re-reads when commands are added or removed, which is what lets a command palette be a view over the registry instead of a second list to keep in sync.
### useHotkeyStore
#### Props
**`returns`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store you passed, or the shared default store. Use setScope, addScope, removeScope and toggleScope to change which commands are live, and isPressed to test a combination directly.
### usePressedKeys
#### Props
**`store`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store to read from. Defaults to a store shared by every hook that does not name one.
**`returns`**
Type: `Accessor`
Required: false
Default Value: `undefined`
Description: The keys currently held down. Use it when a held key changes what an interaction means, such as Shift to extend a selection.
### useIsKeyPressed
#### Props
**`hotkey`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The key or combination to watch. Passed inside a single object, so the whole argument is MaybeAccessor<{ hotkey, store }>.
**`store`**
Type: `HotkeyStore`
Required: false
Default Value: `undefined`
Description: The store to read from. Defaults to a store shared by every hook that does not name one.
**`returns`**
Type: `Accessor`
Required: false
Default Value: `undefined`
Description: Whether that key or combination is currently held.
### usePlatform
#### Props
**`returns`**
Type: `Accessor`
Required: false
Default Value: `undefined`
Description: The current platform, one of 'mac', 'windows' or 'linux'. Resolves after mount, so server and client render the same markup.
### useFormatHotkey
#### Props
**`returns`**
Type: `(hotkey: string, options?: HotkeyFormatOptions) => string`
Required: false
Default Value: `undefined`
Description: A formatter bound to the current platform, so mod+K renders as ⌘ K on macOS and Ctrl K elsewhere. Prefer this over importing formatHotkey directly inside a component, which reads the platform during render and mismatches on hydration.
### useHotkeyRecorder
#### Props
**`props`**
Type: `MaybeAccessor`
Required: false
Default Value: `undefined`
Description: Accepts onRecord, onCancel, onClear, formatOptions and sequenceTimeoutMs.
**`returns`**
Type: `UseHotkeyRecorderReturn`
Required: false
Default Value: `undefined`
Description: The recorder handle, described below.
### UseHotkeyRecorderReturn
#### Props
**`state`**
Type: `Accessor`
Required: false
Default Value: `undefined`
Description: The recorder state. recording is whether it is listening, value is the hotkey recorded so far.
**`start`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Start listening for key events.
**`stop`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Stop listening and keep the recorded hotkey.
**`cancel`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Stop listening and discard the recorded hotkey. Also triggered by Escape.
**`clear`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Clear the recorded hotkey. Also triggered by Backspace or Delete.
# JSON Tree View
## Anatomy
To set up the JSON tree view correctly, you'll need to understand its anatomy and how we name its parts.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Examples
Learn how to use the `JsonTreeView` component in your project. Let's take a look at the most basic example:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
export const Basic = () => {
return (
} />
)
}
```
### Different Data Types
The JSON tree view can display various JavaScript data types including objects, arrays, primitives, and special values:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
const testArray = [1, 2, 3, 4, 5]
Object.defineProperties(testArray, {
customProperty: { value: 'custom value', enumerable: false, writable: false },
anotherProperty: { value: 42, enumerable: false, writable: false },
})
export const ArrayData = () => {
return (
{
const sparse = []
sparse[0] = 'first'
sparse[5] = 'sixth'
return sparse
})(),
}}
>
} />
)
}
```
### Functions and Methods
Display JavaScript functions, async functions, and generators in your JSON tree:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
const data = [
function sum(a: number, b: number) {
return a + b
},
async (promises: Promise[]) => await Promise.all(promises),
function* generator(a: number) {
while (a > 0) {
yield a - 1
}
},
]
export const Functions = () => {
return (
} />
)
}
```
### Regular Expressions
Regular expressions are displayed with their pattern and flags:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
const data = {
regex: /^[a-z0-9]+/g,
case_insensitive: /^(?:[a-z0-9]+)foo.*?/i,
}
export const Regex = () => {
return (
} />
)
}
```
### Error Objects
Error objects and their stack traces can be visualized:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
const data = new Error('Error')
export const Errors = () => {
return (
} />
)
}
```
### Map and Set Objects
Native JavaScript Map and Set objects are supported:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
const data = new Map([
['name', 'ark-ui-json-tree'],
['license', 'MIT'],
['elements', new Set(['ark-ui', 123, false, true, null, undefined, 456n])],
[
'nested',
new Map([
[
'taglines',
new Set([
{ name: 'ark-ui', feature: 'headless components' },
{ name: 'ark-ui', feature: 'framework agnostic' },
{ name: 'ark-ui', feature: 'accessible by default' },
]),
],
]),
],
])
export const MapAndSet = () => {
return (
} />
)
}
```
### Controlling Expand Level
Use the `defaultExpandedDepth` prop to control how many levels are expanded by default:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
export const ExpandLevel = () => {
return (
} />
)
}
```
### Custom Value Rendering
You can customize how specific values are rendered using the `renderValue` prop. This example shows how to make email
addresses clickable:
```tsx
import { JsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
export const RenderValue = () => {
return (
}
renderValue={(node) => {
if (node.type === 'text' && typeof node.value === 'string' && isEmail(node.value)) {
return (
{node.value}
)
}
}}
/>
)
}
const isEmail = (value: string) => {
const strippedValue = value.replace(/^"(.*)"$/, '$1')
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(strippedValue)
}
```
### Configuration Options
The JSON tree view supports several configuration options to customize the display:
```tsx
} />
```
**Configuration Options:**
- **`quotesOnKeys`**: Whether to show quotes around object keys
- **`showNonenumerable`**: Whether to show non-enumerable properties
- **`maxPreviewItems`**: Maximum number of items to show in object/array previews
- **`collapseStringsAfterLength`**: Collapse strings longer than this length
- **`groupArraysAfterLength`**: Group array items when array is longer than this length
### Using the Root Provider
The `RootProvider` component provides a context for the JSON tree view. It accepts the value of the `useJsonTreeView`
hook. You can leverage it to access the component state and methods from outside the JSON tree view.
```tsx
import { JsonTreeView, useJsonTreeView } from '@ark-ui/solid/json-tree-view'
import { ChevronRightIcon } from 'lucide-solid'
import styles from 'styles/json-tree-view.module.css'
export const RootProvider = () => {
const jsonTreeView = useJsonTreeView({
defaultExpandedDepth: 1,
data: {
name: 'John Doe',
age: 30,
email: 'john.doe@example.com',
tags: ['tag1', 'tag2', 'tag3'],
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
zip: '12345',
},
},
})
return (
} />
)
}
```
> If you're using the `RootProvider` component, you don't need to use the `Root` component.
## API Reference
### JsonTreeViewRoot
#### 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.
**`canRename`**
Type: `(node: any, indexPath: IndexPath) => boolean`
Required: false
Default Value: `undefined`
Description: Function to determine if a node can be renamed
**`checkedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled checked node value
**`collapseStringsAfterLength`**
Type: `number`
Required: false
Default Value: `undefined`
Description: undefined
**`data`**
Type: `{}`
Required: false
Default Value: `undefined`
Description: The data to display in the tree.
**`defaultCheckedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial checked node value when rendered.
Use when you don't need to control the checked node value.
**`defaultExpandedDepth`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The default expand level.
**`defaultExpandedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial expanded node ids when rendered.
Use when you don't need to control the expanded node value.
**`defaultFocusedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial focused node value when rendered.
Use when you don't need to control the focused node value.
**`defaultSelectedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial selected node value when rendered.
Use when you don't need to control the selected node value.
**`expandedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled expanded node ids
**`expandOnClick`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether clicking on a branch should open it or not
**`focusedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The value of the focused node
**`groupArraysAfterLength`**
Type: `number`
Required: false
Default Value: `undefined`
Description: undefined
**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.
**`ids`**
Type: `Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>`
Required: false
Default Value: `undefined`
Description: The ids of the tree elements. Useful for composition.
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`loadChildren`**
Type: `(details: LoadChildrenDetails) => Promise`
Required: false
Default Value: `undefined`
Description: Function to load children for a node asynchronously.
When provided, branches will wait for this promise to resolve before expanding.
**`maxPreviewItems`**
Type: `number`
Required: false
Default Value: `undefined`
Description: undefined
**`onBeforeRename`**
Type: `(details: RenameCompleteDetails) => boolean`
Required: false
Default Value: `undefined`
Description: Called before a rename is completed. Return false to prevent the rename.
**`onCheckedChange`**
Type: `(details: CheckedChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the checked value changes
**`onExpandedChange`**
Type: `(details: ExpandedChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the tree is opened or closed
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the focused node changes
**`onLoadChildrenComplete`**
Type: `(details: LoadChildrenCompleteDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node finishes loading children
**`onLoadChildrenError`**
Type: `(details: LoadChildrenErrorDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when loading children fails for one or more nodes
**`onRenameComplete`**
Type: `(details: RenameCompleteDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node label rename is completed
**`onRenameStart`**
Type: `(details: RenameStartDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when a node starts being renamed
**`onSelectionChange`**
Type: `(details: SelectionChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Called when the selection changes
**`quotesOnKeys`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to show quotes on the keys.
**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to scroll to a specific index.
Useful for virtualized tree views.
**`selectedValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled selected node value
**`selectionMode`**
Type: `'single' | 'multiple'`
Required: false
Default Value: `"single"`
Description: Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected
**`showNonenumerable`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined
**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`typeahead`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the tree supports typeahead search
**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.
### JsonTreeViewRootProvider
#### Props
**`value`**
Type: `UseJsonTreeViewReturn`
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.
### JsonTreeViewTree
#### Props
**`arrow`**
Type: `number | boolean | (string & {}) | Node | ArrayElement`
Required: false
Default Value: `undefined`
Description: The icon to use for the arrow.
**`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.
**`indentGuide`**
Type: `number | boolean | (string & {}) | Node | ArrayElement`
Required: false
Default Value: `undefined`
Description: The indent guide to use for the tree.
**`renderValue`**
Type: `(node: JsonNodeHastElement) => Element`
Required: false
Default Value: `undefined`
Description: The function to render the value of the node.
## Accessibility
The JSON tree view is built on top of the Tree View component and complies with the
[Tree View WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).
### Keyboard Support
**`Tab`**
Description: Moves focus to the tree view, placing the first tree view item in focus.
**`Enter + Space`**
Description: Selects the item or branch node
**`ArrowDown`**
Description: Moves focus to the next node
**`ArrowUp`**
Description: Moves focus to the previous node
**`ArrowRight`**
Description: When focus is on a closed branch node, opens the branch. When focus is on an open branch node, moves focus to the first item node.
**`ArrowLeft`**
Description: When focus is on an open branch node, closes the node. When focus is on an item or branch node, moves focus to its parent branch node.
**`Home`**
Description: Moves focus to first node without opening or closing a node.
**`End`**
Description: Moves focus to the last node that can be focused without expanding any nodes that are closed.
**`a-z + A-Z`**
Description: Focus moves to the next node with a name that starts with the typed character. The search logic ignores nodes that are descendants of closed branch.
**`*`**
Description: Expands all sibling nodes that are at the same depth as the focused node.
**`Shift + ArrowDown`**
Description: Moves focus to and toggles the selection state of the next node.
**`Shift + ArrowUp`**
Description: Moves focus to and toggles the selection state of the previous node.
**`Ctrl + A`**
Description: Selects all nodes in the tree. If all nodes are selected, unselects all nodes.
# Locale
## Setup
The `LocaleProvider` component sets the locale for your app, formatting dates, numbers, and other locale-specific data.
> **Note:** If no `LocaleProvider` is setup, the default locale for the app will be `en-US` and therefore the direction
> will be `ltr`.
```tsx
import { LocaleProvider } from '@ark-ui/solid/locale'
export const App = () => {
return {/* Your App */}
}
```
## Usage
To access the current locale and direction settings, use the `useLocaleContext` hook.
```tsx
import { useLocaleContext } from '@ark-ui/solid/locale'
export const Usage = () => {
const locale = useLocaleContext()
return {JSON.stringify(locale(), null, 2)}
}
```
## API Reference
### LocaleProvider
#### Props
**`locale`**
Type: `string`
Required: true
Default Value: `'en-US'`
Description: The locale to use for the application.
# Presence
## Examples
By default the child component starts out as hidden and remains hidden after the `present` state is toggled off. This is
useful for situations where the element needs to be hidden initially and continue to stay hidden after its presence is
no longer required.
```tsx
import { Presence } from '@ark-ui/solid/presence'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/presence.module.css'
export const Basic = () => {
const [present, setPresent] = createSignal(false)
return (
setPresent(!present())}>
Toggle
Content
)
}
```
### Lazy Mount
To delay the mounting of a child component until the `present` prop is set to true, use the `lazyMount` prop:
```tsx
import { Presence } from '@ark-ui/solid/presence'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/presence.module.css'
export const LazyMount = () => {
const [present, setPresent] = createSignal(false)
return (
setPresent(!present())}>
Toggle
Lazy Mounted
)
}
```
### Unmount on Exit
To remove the child component from the DOM when it's not present, use the `unmountOnExit` prop:
```tsx
import { Presence } from '@ark-ui/solid/presence'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/presence.module.css'
export const UnmountOnExit = () => {
const [present, setPresent] = createSignal(false)
return (
setPresent(!present())}>
Toggle
Unmount on Exit
)
}
```
### Combining Lazy Mount and Unmount on Exit
Both `lazyMount` and `unmountOnExit` can be combined for a component to be mounted only when it's present and to be
unmounted when it's no longer present:
```tsx
import { Presence } from '@ark-ui/solid/presence'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/presence.module.css'
export const LazyMountAndUnmountOnExit = () => {
const [present, setPresent] = createSignal(false)
return (
setPresent(!present())}>
Toggle
Lazy + Unmount
)
}
```
## API Reference
### Presence
#### 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.
**`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.
# Swap
## Anatomy
```tsx
```
## Examples
### Fade
Swap between two icons with a fade animation. Set the `swap` prop to toggle between the `on` and `off` indicators.
```tsx
import { Swap } from '@ark-ui/solid/swap'
import { CheckIcon, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/swap.module.css'
export const Fade = () => {
const [swapped, setSwapped] = createSignal(false)
return (
setSwapped((prev) => !prev)}>
)
}
```
### Flip
Add a 3D flip effect by setting `perspective` on the root and using `rotateY` keyframes on the indicators.
```tsx
import { Swap } from '@ark-ui/solid/swap'
import { PauseIcon, PlayIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/swap.module.css'
export const Flip = () => {
const [swapped, setSwapped] = createSignal(false)
return (
setSwapped((prev) => !prev)}>
)
}
```
### Rotate
Rotate the indicators in and out with a spin transition.
```tsx
import { Swap } from '@ark-ui/solid/swap'
import { MoonIcon, SunIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/swap.module.css'
export const Rotate = () => {
const [swapped, setSwapped] = createSignal(false)
return (
setSwapped((prev) => !prev)}>
)
}
```
### Scale
Scale the indicators up and down for a pop-in effect.
```tsx
import { Swap } from '@ark-ui/solid/swap'
import { Volume2Icon, VolumeXIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/swap.module.css'
export const Scale = () => {
const [swapped, setSwapped] = createSignal(false)
return (
setSwapped((prev) => !prev)}>
)
}
```
## Guides
### How It Works
Swap renders two indicators stacked on top of each other in a 1x1 CSS grid. The `swap` prop controls which indicator is
visible. Each indicator uses the presence system, so you get `data-state="open"` and `data-state="closed"` attributes to
drive your CSS animations.
### Animating Indicators
Target `data-state` on each indicator to define enter and exit animations:
```css
.indicator[data-state='open'] {
animation: fade-in 200ms ease-out;
}
.indicator[data-state='closed'] {
animation: fade-out 100ms ease-in;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
```
You can combine animations for richer effects. For example, scale with fade:
```css
.indicator[data-state='open'] {
animation:
scale-in 200ms ease-out,
fade-in 200ms ease-out;
}
.indicator[data-state='closed'] {
animation:
scale-out 100ms ease-in,
fade-out 100ms ease-in;
}
```
### 3D Flip Animation
For a flip effect, set `perspective` on the root and use `backface-visibility: hidden` on indicators:
```css
.flip-indicator {
backface-visibility: hidden;
}
.flip-indicator[data-state='open'] {
animation: flip-in 400ms ease;
}
.flip-indicator[data-state='closed'] {
animation: flip-out 200ms ease;
}
@keyframes flip-in {
from {
transform: rotateY(180deg);
}
to {
transform: rotateY(0deg);
}
}
@keyframes flip-out {
from {
transform: rotateY(0deg);
}
to {
transform: rotateY(180deg);
}
}
```
### Lazy Mount
Use `lazyMount` and `unmountOnExit` to control when indicators mount and unmount. This keeps the DOM clean when
indicators aren't visible.
```tsx
...
...
```
## API Reference
### Props
### Root
#### 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.
**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting
**`swap`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether the swap is in the "on" state.
**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.
### Indicator
#### Props
**`type`**
Type: `'on' | 'off'`
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.
### RootProvider
#### Props
**`value`**
Type: `Accessor`
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.
### Context