From 6df5cb8d657781a77d95a40a6fe98e372a1685fc Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 2 Jun 2026 16:20:59 -0500 Subject: [PATCH 01/56] init new hooks docs: useButton, useListBox, useSelect --- .../pages/react-aria/Button/useButton.mdx | 76 ++++++++ .../pages/react-aria/ListBox/useListBox.mdx | 145 ++++++++++++++ .../pages/react-aria/Select/useSelect.mdx | 184 ++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 packages/dev/s2-docs/pages/react-aria/Button/useButton.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/ListBox/useListBox.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/Select/useSelect.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Button/useButton.mdx b/packages/dev/s2-docs/pages/react-aria/Button/useButton.mdx new file mode 100644 index 00000000000..afe3cd22c7e --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Button/useButton.mdx @@ -0,0 +1,76 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/button'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a button.'; +export const isSubpage = true; + +# useButton + +{docs.exports.useButton.description} + +```tsx render +"use client"; +import React from 'react'; +import {useButton} from 'react-aria/useButton'; + +function Button(props) { + let ref = React.useRef(null); + let {buttonProps, isPressed} = useButton(props, ref); + + return ( + + ); +} + + +``` + +## Features + +On the surface, building a custom styled button seems simple. However, there are many cross browser inconsistencies in interactions and accessibility features to consider. `useButton` handles all of these interactions for you, so you can focus on the styling. It follows the [ARIA button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/). + +* Native HTML ` + {state.isOpen && + + + + } + + ); +} + +///- begin collapse -/// +function Button(props) { + let ref = props.buttonRef; + let {buttonProps} = useButton(props, ref); + return ; +} + +function ListBox(props) { + let ref = React.useRef(null); + let {listBoxRef = ref, state} = props; + let {listBoxProps} = useListBox(props, state, listBoxRef); + + return ( + + ); +} + +function Option({item, state}) { + let ref = React.useRef(null); + let {optionProps, isSelected, isFocused, isDisabled} = useOption({key: item.key}, state, ref); + + return ( +
  • + {item.rendered} + {isSelected ? : null} +
  • + ); +} + +function Popover({children, state, ...props}) { + let popoverRef = React.useRef(null); + let {popoverProps, underlayProps} = usePopover({...props, popoverRef}, state); + + return ( + +
    +
    + + {children} + +
    + + ); +} +///- end collapse -/// + + +``` + +## Features + +A select can be built using the [<select>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) and [<option>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) HTML elements, but this is not possible to style consistently cross browser, especially the options. `useSelect` helps achieve accessible select components that can be styled as needed without compromising on high quality interactions. It follows the [ARIA listbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/). + +* Exposed to assistive technology as a button with a `listbox` popup using ARIA (combined with [useListBox](../ListBox/useListBox.html)) +* Support for selecting a single option +* Support for disabled options +* Support for sections +* Labeling support for accessibility +* Support for native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and server-side validation errors +* Support for mouse, touch, and keyboard interactions +* Tab stop focus management +* Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly +* Typeahead to allow selecting options by typing text, even without opening the listbox +* Browser autofill integration via a hidden native ``, see [useSelect](../Select/useSelect.html). +Note: `useListBox` only handles the list itself. For a dropdown similar to a `` to enable browser form autofill. The `Button`, `ListBox`, and `Popover` components shown collapsed below can be shared with other components such as a ComboBox or Menu. +This example combines `useSelect` with [useButton](../Button/useButton) for the trigger, [useListBox](../ListBox/useListBox) for the popup, and [usePopover](../Popover) for positioning. A <`HiddenSelect`> renders a hidden native `` to enable browser form autofill. The `Button`, `ListBox`, and `Popover` components shown collapsed below can be shared with other components such as a ComboBox or Menu. +This example uses `useSelect` to provide the behavior for a custom select, and reuses the [Button](../Button), [Popover](../Popover), and [ListBox](../ListBox) components from React Aria Components to render the trigger, popup, and list of options. The values returned by the hook are passed to these components via context, following the [Reusing children](../customization#reusing-children) pattern. A <`HiddenSelect`> renders a hidden native ` - Red - Orange - Yellow - Green - Blue - Purple - Black - White + + ``` From 658342e312743e259b762397c21bb90e9444f4e0 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 10:30:09 -0500 Subject: [PATCH 05/56] add useComboBox docs --- .../dev/s2-docs/pages/react-aria/ComboBox.mdx | 2 +- .../pages/react-aria/ComboBox/useComboBox.mdx | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/ComboBox/useComboBox.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx index 78e98c2ebe2..283b63b3835 100644 --- a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx @@ -10,7 +10,7 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['autocomplete', 'search', 'typeahead', 'input']; export const relatedPages = [ - {title: 'useComboBox', url: 'ComboBox/useComboBox.html'}, + {title: 'useComboBox', url: './ComboBox/useComboBox'}, {title: 'Testing ComboBox', url: './ComboBox/testing'} ]; export const description = 'Combines a text input with a listbox, allowing users to filter a list of options to items matching a query.'; diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox/useComboBox.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox/useComboBox.mdx new file mode 100644 index 00000000000..d2c0e3c426f --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox/useComboBox.mdx @@ -0,0 +1,151 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/combobox'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a combo box component.'; +export const isSubpage = true; + +# useComboBox + +{docs.exports.useComboBox.description} + +This example uses `useComboBox` to provide the behavior for a custom combo box, and reuses the [Input](../TextField), [Button](../Button), [Popover](../Popover), and [ListBox](../ListBox) components from React Aria Components to render the text field, trigger, popup, and list of options. The values returned by the hook are passed to these components via context, following the [Reusing children](../customization#reusing-children) pattern. A "contains" filter from [useFilter](../useFilter) filters the options as the user types. + +```tsx render +"use client"; +import React from 'react'; +import {useComboBox} from 'react-aria/useComboBox'; +import {useFilter} from 'react-aria/useFilter'; +import {useComboBoxState} from 'react-stately/useComboBoxState'; +import {CollectionBuilder} from 'react-aria/CollectionBuilder'; +import {Provider} from 'react-aria-components/slots'; +import {ComboBoxStateContext} from 'react-aria-components/ComboBox'; +import {Collection} from 'react-aria-components/Collection'; +import {LabelContext} from 'react-aria-components/Label'; +import {ButtonContext} from 'react-aria-components/Button'; +import {Input, InputContext} from 'react-aria-components/Input'; +import {OverlayTriggerStateContext} from 'react-aria-components/Dialog'; +import {PopoverContext} from 'react-aria-components/Popover'; +import {ListBoxContext, ListStateContext} from 'react-aria-components/ListBox'; +import {Label, FieldButton} from 'vanilla-starter/Form'; +import {Popover} from 'vanilla-starter/Popover'; +import {ComboBoxListBox, ComboBoxItem} from 'vanilla-starter/ComboBox'; +import {ChevronDown} from 'lucide-react'; +import 'vanilla-starter/ComboBox.css'; + +function ComboBox(props) { + // useComboBoxState needs a collection built from the items. CollectionBuilder + // renders a hidden copy of them to construct it before rendering. + return ( + {props.children}}> + {collection => } + + ); +} + +function ComboBoxInner({collection, ...props}) { + // Create state based on the collection and incoming props. A "contains" + // filter from useFilter filters the options against the current input text. + let {contains} = useFilter({sensitivity: 'base'}); + let state = useComboBoxState({...props, defaultFilter: contains, collection, children: undefined}); + + // Get props for child elements from useComboBox. + let fieldRef = React.useRef(null); + let inputRef = React.useRef(null); + let buttonRef = React.useRef(null); + let listBoxRef = React.useRef(null); + let popoverRef = React.useRef(null); + let {labelProps, inputProps, buttonProps, listBoxProps} = + useComboBox({...props, inputRef, buttonRef, listBoxRef, popoverRef}, state); + + // Provide the values returned by the hook to the reused components via context. + return ( + +
    + +
    + + + + +
    + + {/* Rendered from the collection in state, provided via ListStateContext. */} + + +
    +
    + ); +} + + + Aardvark + Cat + Dog + Kangaroo + Panda + Snake + +``` + +## Features + +A combo box can be built using the [<datalist>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) HTML element, but this is limited in functionality and difficult to style. `useComboBox` helps achieve accessible combo box and autocomplete components that can be styled as needed. It follows the [ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). + +* Support for filtering a list of options by typing +* Support for selecting a single option +* Support for disabled options +* Support for groups of items in sections +* Support for custom user input values +* Support for controlled and uncontrolled options, selection, input value, and open state +* Support for custom filter functions +* Async loading and infinite scrolling support +* Support for virtualized lists +* Exposed to assistive technology as a `combobox` with ARIA +* Labeling support for accessibility +* Required and invalid states exposed to assistive technology via ARIA +* Support for mouse, touch, and keyboard interactions +* Keyboard support for opening the list box using the arrow keys, including automatically focusing the first or last item accordingly +* Support for opening the list box when typing, on focus, or manually +* Custom localized announcements for option focusing, filtering, and selection using an ARIA live region to work around VoiceOver bugs +* Support for native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and server-side validation errors + +## State management + +`useComboBox` requires knowledge of the options in the combo box in order to handle keyboard navigation and other interactions. It does this using the `Collection` interface, which is a generic interface to access sequential unique keyed data. You can implement this interface yourself, e.g. by using a prop to pass a list of item objects, but `useComboBoxState` from `react-stately` implements a JSX based interface for building collections instead. See [Collection Components](../collections) for more information. + +In addition, `useComboBoxState` manages the state necessary for single selection and exposes a `SelectionManager`, which makes use of the collection to provide an interface to update the selection state. It also holds state to track if the popup is open, if the combo box is focused, and the current input value. For more information about selection, see [Selection](../selection). + +## API + + + +### AriaComboBoxOptions + + + +### ComboBoxAria + + From 2c02c667092b3205b881808f52b00f92aebc5dea Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 10:57:04 -0500 Subject: [PATCH 06/56] add useMenu docs --- .../dev/s2-docs/pages/react-aria/Menu.mdx | 2 +- .../s2-docs/pages/react-aria/Menu/useMenu.mdx | 174 ++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Menu/useMenu.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Menu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu.mdx index 129aa99c90a..2ba97ddd1a8 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu.mdx @@ -8,7 +8,7 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['dropdown']; export const relatedPages = [ - {title: 'useMenu', url: 'Menu/useMenu.html'}, + {title: 'useMenu', url: './Menu/useMenu'}, {title: 'Testing Menu', url: './Menu/testing'} ]; export const description = 'Displays a list of actions or options that a user can choose.'; diff --git a/packages/dev/s2-docs/pages/react-aria/Menu/useMenu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu/useMenu.mdx new file mode 100644 index 00000000000..d23661e28b9 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Menu/useMenu.mdx @@ -0,0 +1,174 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/menu'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a menu component.'; +export const isSubpage = true; + +# useMenu + +{docs.exports.useMenu.description} + +This example uses `useMenuTrigger` to build a menu button, and reuses the [Button](../Button) and [Popover](../Popover) components from React Aria Components to render the trigger and popup. The trigger props returned by the hook are passed to these components via context, following the [Reusing children](../customization#reusing-children) pattern. The menu itself is built from scratch with `useMenu` and `useMenuItem`, since those hooks are what a `Menu` component is composed from. + +```tsx render +"use client"; +import React from 'react'; +import {useMenuTrigger, useMenu, useMenuItem} from 'react-aria/useMenu'; +import {useMenuTriggerState} from 'react-stately/useMenuTriggerState'; +import {useTreeState} from 'react-stately/useTreeState'; +import {Item} from 'react-stately/Item'; +import {Provider} from 'react-aria-components/slots'; +import {OverlayTriggerStateContext} from 'react-aria-components/Dialog'; +import {PopoverContext} from 'react-aria-components/Popover'; +import {ButtonContext} from 'react-aria-components/Button'; +import {Button} from 'vanilla-starter/Button'; +import {Popover} from 'vanilla-starter/Popover'; +import {Ellipsis} from 'lucide-react'; + +function MenuButton(props) { + // Create state to track whether the menu is open. + let state = useMenuTriggerState(props); + + // Get props for the menu trigger and menu elements. + let ref = React.useRef(null); + let {menuTriggerProps, menuProps} = useMenuTrigger({}, state, ref); + + // Provide the trigger props to the reused Button and Popover via context. + return ( + + + +
    + + + ); +} + +function Menu(props) { + // Create menu state based on the incoming props, building a collection from the items. + let state = useTreeState(props); + + // Get props for the menu element. + let ref = React.useRef(null); + let {menuProps} = useMenu(props, state, ref); + + return ( +
      + {[...state.collection].map(item => ( + + ))} +
    + ); +} + +function MenuItem({item, state}) { + // Get props for the individual menu item. + let ref = React.useRef(null); + let {menuItemProps, isFocused, isDisabled} = useMenuItem({key: item.key}, state, ref); + + return ( +
  • + {item.rendered} +
  • + ); +} + + alert(key)}> + Copy + Cut + Paste + +``` + +## Features + +There is no native element to implement a menu in HTML that is widely supported. `useMenuTrigger` and `useMenu` help achieve accessible menu components that can be styled as needed. It follows the [ARIA menu pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu/). + +* Exposed to assistive technology as a button with a `menu` using ARIA +* Support for single, multiple, or no selection +* Support for disabled items +* Support for sections +* Complex item labeling support for accessibility +* Keyboard navigation support including arrow keys, home/end, page up/down +* Automatic scrolling support during keyboard navigation +* Keyboard support for opening the menu using the arrow keys, including automatically focusing the first or last item accordingly +* Typeahead to allow focusing items by typing text +* Support for use with virtualized lists + +## State management + +A menu trigger consists of a button or other trigger element combined with a popup menu containing a list of items or groups. State for the trigger is managed by the `useMenuTriggerState` hook from `react-stately`, which tracks whether the menu is open. State for the menu itself is managed by the `useTreeState` hook from `react-stately`, which builds a `Collection` from the items and exposes a `SelectionManager` to handle selection. See [Collection Components](../collections) for more information about collections, and [Selection](../selection) for more about selection. + +If a menu, menu item, or group does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to identify the element to assistive technology. + +## API + + + + + + +### AriaMenuTriggerProps + + + +### MenuTriggerAria + + + +### AriaMenuOptions + + + +### MenuAria + + + +### AriaMenuItemProps + + + +### MenuItemAria + + + +### AriaMenuSectionProps + + + +### MenuSectionAria + + From 6cb6d5236ef708f84287d4622c4bc90323ee1213 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 10:57:21 -0500 Subject: [PATCH 07/56] add useDatePicker docs --- .../s2-docs/pages/react-aria/DatePicker.mdx | 2 +- .../react-aria/DatePicker/useDatePicker.mdx | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx index 2e3b34f81fd..a097c9408f8 100644 --- a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/datepicker/datepicker-anatomy.svg'; export const tags = ['calendar', 'input']; -export const relatedPages = [{'title': 'useDatePicker', 'url': 'DatePicker/useDatePicker.html'}]; +export const relatedPages = [{'title': 'useDatePicker', 'url': './DatePicker/useDatePicker'}]; export const description = 'Combines a DateField and a Calendar popover to allow users to enter or select a date and time value.'; # DatePicker diff --git a/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx new file mode 100644 index 00000000000..c61773a8267 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx @@ -0,0 +1,118 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/datepicker'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a date picker component.'; +export const isSubpage = true; + +# useDatePicker + +{docs.exports.useDatePicker.description} + +This example uses `useDatePicker` to combine a date field and a calendar popover, and reuses the [DateField](../DateField), [Button](../Button), [Popover](../Popover), and [Calendar](../Calendar) components from React Aria Components to render each part. The values returned by the hook are passed to these components via context, following the [Reusing children](../customization#reusing-children) pattern. Date values use the [@internationalized/date](../internationalized/date/) library. + +```tsx render +"use client"; +import React from 'react'; +import {useDatePicker} from 'react-aria/useDatePicker'; +import {useDatePickerState} from 'react-stately/useDatePickerState'; +import {Provider} from 'react-aria-components/slots'; +import {Group, GroupContext} from 'react-aria-components/Group'; +import {DateFieldContext} from 'react-aria-components/DateField'; +import {ButtonContext} from 'react-aria-components/Button'; +import {LabelContext} from 'react-aria-components/Label'; +import {CalendarContext} from 'react-aria-components/Calendar'; +import {DialogContext, OverlayTriggerStateContext} from 'react-aria-components/Dialog'; +import {PopoverContext} from 'react-aria-components/Popover'; +import {DateInput, DateSegment} from 'vanilla-starter/DateField'; +import {Label, FieldButton} from 'vanilla-starter/Form'; +import {Calendar} from 'vanilla-starter/Calendar'; +import {Popover} from 'vanilla-starter/Popover'; +import {ChevronDown} from 'lucide-react'; +import 'vanilla-starter/DatePicker.css'; + +function DatePicker(props) { + // Create state based on the incoming props. + let state = useDatePickerState(props); + + // Get props for the child elements from useDatePicker. + let groupRef = React.useRef(null); + let {groupProps, labelProps, fieldProps, buttonProps, dialogProps, calendarProps} = + useDatePicker(props, state, groupRef); + + // Provide the values returned by the hook to the reused components via context. + return ( + +
    + + + {/* The DateInput builds its own field state from fieldProps, provided via DateFieldContext. */} + {segment => } + + + + + + {/* Rendered from calendarProps, provided via CalendarContext. */} + + +
    +
    + ); +} + + +``` + +## Features + +A date picker can be built using ``, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useDatePicker` helps achieve accessible and international date and time pickers that can be styled as needed. + +* **Dates and times** – Support for dates and times with configurable granularity. +* **International** – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more. Locale-specific formatting, number systems, hour cycles, and right-to-left support are available as well. +* **Time zone aware** – Dates and times can optionally include a time zone. All modifications follow time zone rules such as daylight saving time. +* **Accessible** – Each date and time unit is displayed as an individually focusable and editable segment, which allows users an easy way to edit dates using the keyboard, in any date format and locale. Users can also open a calendar popover to select dates in a standard month grid. Localized screen reader messages are included to announce when the selection and visible date range change. +* **Touch friendly** – Date segments are editable using an easy to use numeric keypad, and all interactions are accessible using touch-based screen readers. +* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. + +## Anatomy + +A date picker consists of a label, and a group containing a [date field](../DateField) and a button. Clicking the button opens a [calendar](../Calendar) popup. The date field includes segments representing each unit of a date and time (e.g. years, months, days, etc.), each of which is individually focusable and editable using the keyboard. The calendar popup offers a more visual way of choosing a date. + +State is managed by the `useDatePickerState` hook from `react-stately`, which holds the selected date value and tracks whether the calendar popover is open. The state object should be passed as an argument to `useDatePicker`. + +If the date picker does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to identify it to assistive technology. + +## API + + + +### AriaDatePickerProps + + + +### DatePickerAria + + From 4167b7546bcc30cc7eae586a6fc64487ba6b4b30 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 11:10:41 -0500 Subject: [PATCH 08/56] add useDateRangePicker docs --- .../pages/react-aria/DateRangePicker.mdx | 2 +- .../DateRangePicker/useDateRangePicker.mdx | 129 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx index 6019367080a..c47b5b8912d 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/datepicker/daterangepicker-anatomy.svg'; export const tags = ['calendar', 'input']; -export const relatedPages = [{'title': 'useDateRangePicker', 'url': 'DateRangePicker/useDateRangePicker.html'}]; +export const relatedPages = [{'title': 'useDateRangePicker', 'url': './DateRangePicker/useDateRangePicker'}]; export const description = 'Combines two DateFields and a RangeCalendar popover to allow users to enter or select a date range.'; # DateRangePicker diff --git a/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx new file mode 100644 index 00000000000..bc4f2e228fa --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx @@ -0,0 +1,129 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/datepicker'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a date range picker component.'; +export const isSubpage = true; + +# useDateRangePicker + +{docs.exports.useDateRangePicker.description} + +This example uses `useDateRangePicker` to combine two date fields and a calendar popover, and reuses the [DateField](../DateField), [Button](../Button), [Popover](../Popover), and [RangeCalendar](../RangeCalendar) components from React Aria Components to render each part. The values returned by the hook are passed to these components via context, following the [Reusing children](../customization#reusing-children) pattern. The start and end fields are provided as `start` and `end` slots on the `DateFieldContext`. Date values use the [@internationalized/date](../internationalized/date/) library. + +```tsx render +"use client"; +import React from 'react'; +import {useDateRangePicker} from 'react-aria/useDateRangePicker'; +import {useDateRangePickerState} from 'react-stately/useDateRangePickerState'; +import {Provider} from 'react-aria-components/slots'; +import {Group, GroupContext} from 'react-aria-components/Group'; +import {DateFieldContext} from 'react-aria-components/DateField'; +import {ButtonContext} from 'react-aria-components/Button'; +import {LabelContext} from 'react-aria-components/Label'; +import {RangeCalendarContext} from 'react-aria-components/RangeCalendar'; +import {DialogContext, OverlayTriggerStateContext} from 'react-aria-components/Dialog'; +import {PopoverContext} from 'react-aria-components/Popover'; +import {DateInput, DateSegment} from 'vanilla-starter/DateField'; +import {Label, FieldButton} from 'vanilla-starter/Form'; +import {RangeCalendar} from 'vanilla-starter/RangeCalendar'; +import {Popover} from 'vanilla-starter/Popover'; +import {ChevronDown} from 'lucide-react'; +import 'vanilla-starter/DateRangePicker.css'; + +function DateRangePicker(props) { + // Create state based on the incoming props. + let state = useDateRangePickerState(props); + + // Get props for the child elements from useDateRangePicker. + let groupRef = React.useRef(null); + let { + groupProps, + labelProps, + startFieldProps, + endFieldProps, + buttonProps, + dialogProps, + calendarProps + } = useDateRangePicker(props, state, groupRef); + + // Provide the values returned by the hook to the reused components via context. + return ( + +
    + + +
    + {segment => } + + {segment => } +
    + + + +
    + + {/* Rendered from calendarProps, provided via RangeCalendarContext. */} + + +
    +
    + ); +} + + +``` + +## Features + +A date range picker can be built using two separate `` elements, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useDateRangePicker` helps achieve accessible and international date and time range pickers that can be styled as needed. + +* **Dates and times** – Support for dates and times with configurable granularity. +* **International** – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more. Locale-specific formatting, number systems, hour cycles, and right-to-left support are available as well. +* **Time zone aware** – Dates and times can optionally include a time zone. All modifications follow time zone rules such as daylight saving time. +* **Accessible** – Each date and time unit is displayed as an individually focusable and editable segment, which allows users an easy way to edit dates using the keyboard, in any date format and locale. Users can also open a calendar popover to select date ranges in a standard month grid. Localized screen reader messages are included to announce when the selection and visible date range change. +* **Touch friendly** – Date segments are editable using an easy to use numeric keypad, date ranges can be selected by dragging over dates in the calendar using a touch screen, and all interactions are accessible using touch-based screen readers. +* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. + +## Anatomy + +A date range picker consists of a label, and a group containing two [date fields](../DateField) (a start and an end) and a button. Clicking the button opens a [range calendar](../RangeCalendar) popup. Each date field includes segments representing each unit of a date and time (e.g. years, months, days, etc.), each of which is individually focusable and editable using the keyboard. The calendar popup offers a more visual way of choosing a date range. + +State is managed by the `useDateRangePickerState` hook from `react-stately`, which holds the selected range value and tracks whether the calendar popover is open. The state object should be passed as an argument to `useDateRangePicker`. + +If the date range picker does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to identify it to assistive technology. + +## API + + + +### AriaDateRangePickerProps + + + +### DateRangePickerAria + + From d106bf93495fcdced364f23b16927762f41d5af3 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 11:48:52 -0500 Subject: [PATCH 09/56] add usePopover docs --- .../dev/s2-docs/pages/react-aria/Popover.mdx | 2 +- .../pages/react-aria/Popover/usePopover.mdx | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Popover.mdx b/packages/dev/s2-docs/pages/react-aria/Popover.mdx index df3cae64dff..7c57f16766f 100644 --- a/packages/dev/s2-docs/pages/react-aria/Popover.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Popover.mdx @@ -8,7 +8,7 @@ import Anatomy from '/packages/react-aria/docs/overlays/popover-anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['popup', 'overlay']; -export const relatedPages = [{'title': 'usePopover', 'url': 'Popover/usePopover.html'}]; +export const relatedPages = [{'title': 'usePopover', 'url': './Popover/usePopover'}]; export const description = 'An overlay element positioned relative to a trigger.'; # Popover diff --git a/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx b/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx new file mode 100644 index 00000000000..6de425c2808 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx @@ -0,0 +1,121 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/overlays'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a popover overlay.'; +export const isSubpage = true; + +# usePopover + +{docs.exports.usePopover.description} + +`usePopover` is the popover primitive itself, so the overlay is built from scratch using an [<Overlay>](../Popover) portal and <`DismissButton`> elements. The trigger reuses the [Button](../Button) component, and the content reuses the [Dialog](../Modal/useDialog) component, both from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {usePopover} from 'react-aria/usePopover'; +import {useOverlayTrigger} from 'react-aria/useOverlayTrigger'; +import {Overlay, DismissButton} from 'react-aria/Overlay'; +import {useOverlayTriggerState} from 'react-stately/useOverlayTriggerState'; +import {Provider} from 'react-aria-components/slots'; +import {ButtonContext} from 'react-aria-components/Button'; +import {Button} from 'vanilla-starter/Button'; +import {Dialog, Heading} from 'vanilla-starter/Dialog'; + +function Popover({state, children, triggerRef, ...props}) { + // usePopover positions the overlay relative to the trigger and closes it on + // outside interaction or Escape. The state comes from useOverlayTriggerState. + let popoverRef = React.useRef(null); + let {popoverProps, underlayProps} = usePopover({...props, triggerRef, popoverRef}, state); + + return ( + // Overlay renders into a portal and contains focus while the popover is open. + +
    +
    + {/* Visually hidden buttons let touch screen reader users dismiss the popover. */} + + {children} + +
    + + ); +} + +function PopoverTrigger({label, children, ...props}) { + // useOverlayTrigger connects the trigger button to the popover via ARIA. + let triggerRef = React.useRef(null); + let state = useOverlayTriggerState(props); + let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state, triggerRef); + + return ( + <> + {/* Provide the trigger props to the reused Button via context. */} + + + + {state.isOpen && + + {React.cloneElement(children, overlayProps)} + } + + ); +} + + + + Popover title +

    This is the content of the popover.

    +
    +
    +``` + +## Features + +There is no built in way to create popovers in HTML. `usePopover` helps achieve accessible popovers that can be styled as needed. + +* **Accessible** – The trigger and popover are automatically associated semantically via ARIA. Content outside the popover is hidden from assistive technologies while it is open. The popover closes when interacting outside, or pressing the Escape key. +* **Focus management** – Focus is moved into the popover on mount, and restored to the trigger element on unmount. +* **Positioning** – The popover is positioned relative to the trigger element, and automatically flips and adjusts to avoid overlapping with the edge of the browser window. Scrolling is prevented outside the popover to avoid unintentionally repositioning or closing it. + +**Note**: `usePopover` only handles the overlay itself. It should be combined with [useDialog](../Modal/useDialog) to create fully accessible popovers. Other overlays such as menus may also be placed in a popover. + +## Anatomy + +A popover consists of a trigger element (e.g. button) and an overlay, which is positioned relative to the trigger. The overlay may contain a [dialog](../Modal/useDialog), or another element such as a [menu](../Menu/useMenu) or [listbox](../ListBox/useListBox) when used within a component such as a [select](../Select/useSelect) or [combobox](../ComboBox/useComboBox). + +State is managed by the `useOverlayTriggerState` hook in `react-stately`. The state object should be passed as an argument to `usePopover`. + +## API + + + +### AriaPopoverProps + + + +### PopoverAria + + From 4189ee118bf868ccfbbe0e07eb0ad706fd72dc7e Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 11:49:06 -0500 Subject: [PATCH 10/56] add useModalOverlay docs --- .../dev/s2-docs/pages/react-aria/Modal.mdx | 5 +- .../react-aria/Modal/useModalOverlay.mdx | 130 ++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Modal.mdx b/packages/dev/s2-docs/pages/react-aria/Modal.mdx index 4e905dad708..4e05d196314 100644 --- a/packages/dev/s2-docs/pages/react-aria/Modal.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Modal.mdx @@ -8,7 +8,10 @@ import Anatomy from '/packages/react-aria/docs/overlays/modal-anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['dialog', 'popup', 'overlay']; -export const relatedPages = [{'title': 'useModalOverlay', 'url': 'Modal/useModalOverlay.html'}]; +export const relatedPages = [ + {'title': 'useModalOverlay', 'url': './Modal/useModalOverlay'}, + {'title': 'useDialog', 'url': './Modal/useDialog'} +]; export const description = 'An overlay element which blocks interaction with elements outside it.'; # Modal diff --git a/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx b/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx new file mode 100644 index 00000000000..127477d832d --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx @@ -0,0 +1,130 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/overlays'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a modal overlay.'; +export const isSubpage = true; + +# useModalOverlay + +{docs.exports.useModalOverlay.description} + +`useModalOverlay` is the modal primitive itself, so the overlay and underlay are built from scratch using an [<Overlay>](../Popover) portal. The trigger and close action reuse the [Button](../Button) component, and the content reuses the [Dialog](../Modal/useDialog) component, both from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useModalOverlay} from 'react-aria/useModalOverlay'; +import {useOverlayTrigger} from 'react-aria/useOverlayTrigger'; +import {Overlay} from 'react-aria/Overlay'; +import {useOverlayTriggerState} from 'react-stately/useOverlayTriggerState'; +import {Provider} from 'react-aria-components/slots'; +import {ButtonContext} from 'react-aria-components/Button'; +import {Button} from 'vanilla-starter/Button'; +import {Dialog, Heading} from 'vanilla-starter/Dialog'; + +function Modal({state, children, ...props}) { + // useModalOverlay handles scroll locking, focus containment, and optional + // dismiss behavior. The state comes from useOverlayTriggerState. + let ref = React.useRef(null); + let {modalProps, underlayProps} = useModalOverlay(props, state, ref); + + return ( + // Overlay renders into a portal and contains focus while the modal is open. + +
    +
    + {children} +
    +
    +
    + ); +} + +function ModalTrigger({label, children, ...props}) { + // useOverlayTrigger connects the trigger button to the modal via ARIA. + let state = useOverlayTriggerState(props); + let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state); + + return ( + <> + {/* Provide the trigger props to the reused Button via context. */} + + + + {state.isOpen && + + {React.cloneElement(children(state.close), overlayProps)} + } + + ); +} + + + {close => + + Enter your name +
    + + + +
    +
    } +
    +``` + +## Features + +The HTML [<dialog>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element can be used to build modal overlays. However, it is not yet widely supported across browsers, and can be difficult to style and customize. `useModalOverlay` helps achieve accessible modal overlays that can be styled as needed. + +* **Accessible** – Content outside the modal is hidden from assistive technologies while it is open. The modal optionally closes when interacting outside, or pressing the Escape key. +* **Focus management** – Focus is moved into the modal on mount, and restored to the trigger element on unmount. While open, focus is contained within the modal, preventing the user from tabbing outside. +* **Scroll locking** – Scrolling the page behind the modal is prevented while it is open, including in mobile browsers. + +**Note**: `useModalOverlay` only handles the overlay itself. It should be combined with [useDialog](../Modal/useDialog) to create fully accessible modal dialogs. Other overlays such as menus may also be placed in a modal overlay. + +## Anatomy + +A modal overlay consists of an overlay container element, and an underlay. The overlay may contain a [dialog](../Modal/useDialog), or another element such as a [menu](../Menu/useMenu) or [listbox](../ListBox/useListBox). The underlay is typically a partially transparent element that covers the rest of the screen behind the overlay, and prevents the user from interacting with the elements behind it. + +State is managed by the `useOverlayTriggerState` hook in `react-stately`. The state object should be passed as an argument to `useModalOverlay`. + +## API + + + +### AriaModalOverlayProps + + + +### ModalOverlayAria + + From 39657ec73cd8b16bd7a4a8559aa3dd36e40ff2ca Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 11:49:14 -0500 Subject: [PATCH 11/56] add useDIalog docs --- .../pages/react-aria/Modal/useDialog.mdx | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 packages/dev/s2-docs/pages/react-aria/Modal/useDialog.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Modal/useDialog.mdx b/packages/dev/s2-docs/pages/react-aria/Modal/useDialog.mdx new file mode 100644 index 00000000000..c02d2307eda --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Modal/useDialog.mdx @@ -0,0 +1,90 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/dialog'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a dialog component.'; +export const isSubpage = true; + +# useDialog + +{docs.exports.useDialog.description} + +`useDialog` is the dialog primitive itself, so the `Dialog` component is built from scratch. To render it interactively, this example reuses the [Modal](../Modal), [Button](../Button), and `DialogTrigger` components from React Aria Components to provide the overlay container and trigger. A dialog may also be placed within a [popover](../Popover/usePopover). + +```tsx render +"use client"; +import React from 'react'; +import {useDialog} from 'react-aria/useDialog'; +import {OverlayTriggerStateContext} from 'react-aria-components/Dialog'; +import {DialogTrigger} from 'vanilla-starter/Dialog'; +import {Modal} from 'vanilla-starter/Modal'; +import {Button} from 'vanilla-starter/Button'; + +function Dialog({title, children, ...props}) { + // useDialog applies the dialog ARIA role and labels it by its title element. + let ref = React.useRef(null); + let {dialogProps, titleProps} = useDialog(props, ref); + + return ( +
    + {title && +

    {title}

    } + {children} +
    + ); +} + +function CloseButton() { + // The trigger state is provided by DialogTrigger via context. + let state = React.useContext(OverlayTriggerStateContext); + return ; +} + + + + + +

    This dialog is built with useDialog.

    + +
    +
    +
    +``` + +## Features + +The HTML [<dialog>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element can be used to build dialogs. However, it is not yet widely supported across browsers, and building fully accessible custom dialogs from scratch is very difficult and error prone. `useDialog` helps achieve accessible dialogs that can be styled as needed. + +* **Flexible** – Dialogs can be used within a [modal](useModalOverlay) or [popover](../Popover/usePopover) to create many types of overlay elements. +* **Accessible** – Exposed to assistive technology as a `dialog` or `alertdialog` with ARIA. The dialog is labeled by its title element, and content outside the dialog is hidden from assistive technologies while it is open. +* **Focus management** – Focus is moved into the dialog on mount, and restored to the trigger element on unmount. While open, focus is contained within the dialog, preventing the user from tabbing outside. + +## Anatomy + +A dialog consists of a container element and an optional title. `useDialog` handles exposing this to assistive technology using ARIA. It can be combined with [useModalOverlay](useModalOverlay) or [usePopover](../Popover/usePopover) to create modal dialogs, popovers, and other types of overlays. + +If a dialog does not have a visible title element, an `aria-label` or `aria-labelledby` prop must be passed instead to identify the element to assistive technology. + +## API + + + +### AriaDialogProps + + + +### DialogAria + + From a4caa5d8d94140d5c7f6990d7a253f43f0ca1903 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 11:49:21 -0500 Subject: [PATCH 12/56] add useTooltipTrigger docs --- .../dev/s2-docs/pages/react-aria/Tooltip.mdx | 2 +- .../react-aria/Tooltip/useTooltipTrigger.mdx | 123 ++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx b/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx index 0164758d269..d02d85148eb 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx @@ -8,7 +8,7 @@ import Anatomy from '/packages/react-aria/docs/tooltip/anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['hint', 'popup', 'info']; -export const relatedPages = [{'title': 'useTooltipTrigger', 'url': 'Tooltip/useTooltipTrigger.html'}]; +export const relatedPages = [{'title': 'useTooltipTrigger', 'url': './Tooltip/useTooltipTrigger'}]; export const description = 'Displays a description of an element on hover or focus.'; # Tooltip diff --git a/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx b/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx new file mode 100644 index 00000000000..d1f33d9c6d3 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx @@ -0,0 +1,123 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/tooltip'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a tooltip component.'; +export const isSubpage = true; + +# useTooltipTrigger + +{docs.exports.useTooltipTrigger.description} + +`useTooltipTrigger` and `useTooltip` are the tooltip primitives, so the tooltip is built from scratch and positioned next to its trigger. The trigger receives the hook's props directly; here it is styled to match the [Button](../Button) component from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useTooltip, useTooltipTrigger} from 'react-aria/useTooltipTrigger'; +import {useTooltipTriggerState} from 'react-stately/useTooltipTriggerState'; +import {mergeProps} from 'react-aria/mergeProps'; +import 'vanilla-starter/Button.css'; + +function Tooltip({state, ...props}) { + // useTooltip applies the tooltip ARIA role and connects it to the trigger. + let {tooltipProps} = useTooltip(props, state); + + return ( + + {props.children} + + ); +} + +function TooltipButton(props) { + let state = useTooltipTriggerState(props); + let ref = React.useRef(null); + + // Get props for the trigger and its tooltip. + let {triggerProps, tooltipProps} = useTooltipTrigger(props, state, ref); + + return ( + + + {state.isOpen && + {props.tooltip}} + + ); +} + +
    + Edit + Delete +
    +``` + +## Features + +The HTML `title` attribute can be used to create a tooltip, but it cannot be styled. Custom styled tooltips can be hard to build in an accessible way. `useTooltipTrigger` and `useTooltip` help build fully accessible tooltips that can be styled as needed. + +* Keyboard focus management and cross browser normalization +* Hover management and cross browser normalization +* Labeling support for screen readers (`aria-describedby`) +* Exposed as a tooltip to assistive technology via ARIA +* Matches native tooltip behavior with delay on hover of first tooltip and no delay on subsequent tooltips + +## Anatomy + +A tooltip consists of two parts: the trigger element and the tooltip itself. Users may reveal the tooltip by hovering or focusing the trigger. + +Tooltip state is managed by the `useTooltipTriggerState` hook in `react-stately`. The state object should be passed as an option to `useTooltipTrigger` and `useTooltip`. + +## API + + + + +### TooltipTriggerProps + + + +### TooltipTriggerAria + + + +### AriaTooltipProps + + + +### TooltipAria + + From b1e091527b1618e98878ea53bbbe29c6afb7212d Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:46:01 -0500 Subject: [PATCH 13/56] add useCalendar docs --- .../dev/s2-docs/pages/react-aria/Calendar.mdx | 2 +- .../pages/react-aria/Calendar/useCalendar.mdx | 166 ++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx index 7a3ec7657ce..95f0cad2472 100644 --- a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/calendar/calendar-anatomy.svg'; export const tags = ['date']; -export const relatedPages = [{'title': 'useCalendar', 'url': 'Calendar/useCalendar.html'}]; +export const relatedPages = [{'title': 'useCalendar', 'url': './Calendar/useCalendar'}]; export const description = 'Displays one or more date grids and allows users to select a single date.'; # Calendar diff --git a/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx new file mode 100644 index 00000000000..86ffac18511 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx @@ -0,0 +1,166 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/calendar'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a calendar component.'; +export const isSubpage = true; + +# useCalendar + +{docs.exports.useCalendar.description} + +A calendar is built from scratch with `useCalendar`, `useCalendarGrid`, and `useCalendarCell`, since those hooks are the calendar primitives. The previous and next navigation buttons reuse the [Button](../Button) component from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useCalendar, useCalendarGrid, useCalendarCell} from 'react-aria/useCalendar'; +import {useCalendarState} from 'react-stately/useCalendarState'; +import {useLocale} from 'react-aria/I18nProvider'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import {createCalendar} from '@internationalized/date'; +import {Button} from 'vanilla-starter/Button'; +import {ChevronLeft, ChevronRight} from 'lucide-react'; +import 'vanilla-starter/Button.css'; + +function Calendar(props) { + // useCalendarState handles date manipulation across calendar systems. + let {locale} = useLocale(); + let state = useCalendarState({...props, locale, createCalendar}); + let {calendarProps, prevButtonProps, nextButtonProps, title} = useCalendar(props, state); + + return ( +
    +
    + +

    {title}

    + +
    + +
    + ); +} + +function CalendarGrid({state, ...props}) { + // useCalendarGrid renders the table of dates for a single month. + let {gridProps, headerProps, weekDays, weeksInMonth} = useCalendarGrid(props, state); + + return ( + + + + {weekDays.map((day, i) => ( + + ))} + + + + {[...new Array(weeksInMonth).keys()].map(weekIndex => ( + + {state.getDatesInWeek(weekIndex).map((date, i) => ( + date + ? + : + ))} + +
    {day}
    + ))} +
    + ); +} + +function CalendarCell({state, date}) { + // useCalendarCell provides the props and state for an individual date cell. + let ref = React.useRef(null); + let {cellProps, buttonProps, isSelected, isOutsideVisibleRange, isDisabled, formattedDate} = + useCalendarCell({date}, state, ref); + + // Show a focus ring when the cell is focused via the keyboard. + let {focusProps, isFocusVisible} = useFocusRing(); + + return ( + + + + ); +} + + +``` + +## Features + +There is no standalone calendar element in HTML. `useCalendar` helps achieve accessible calendar and date picker components that can be styled as needed. + +* Support for selecting a single date +* Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more +* Locale-specific formatting, number systems, and right-to-left support +* Date values are provided using objects from the [@internationalized/date](../internationalized/date/) library +* Support for minimum and maximum values, disabled and unavailable dates +* Localized screen reader messages announce the selected and visible date ranges +* Keyboard navigation between dates with arrow keys, including wrapping and validation +* Full keyboard and screen reader accessibility following the [ARIA grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) + +## State management + +`useCalendar` requires a `state` object created with the `useCalendarState` hook from `react-stately`, which holds the selected date and visible date range, and exposes methods to navigate between months. `useCalendarState` requires a `createCalendar` function from [@internationalized/date](../internationalized/date/), which implements date manipulation across calendar systems. + +## API + + + + + +### AriaCalendarProps + + + +### CalendarAria + + + +### AriaCalendarGridProps + + + +### CalendarGridAria + + + +### AriaCalendarCellProps + + + +### CalendarCellAria + + From 2da1139ccb15d94a2010c40ae89a8528b5900e4d Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:46:11 -0500 Subject: [PATCH 14/56] add useRangeCalendar docs --- .../pages/react-aria/RangeCalendar.mdx | 2 +- .../RangeCalendar/useRangeCalendar.mdx | 159 ++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx index 603ae4efd8d..5cad7f60098 100644 --- a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/calendar/rangecalendar-anatomy.svg'; export const tags = ['calendar']; -export const relatedPages = [{'title': 'useRangeCalendar', 'url': 'RangeCalendar/useRangeCalendar.html'}]; +export const relatedPages = [{'title': 'useRangeCalendar', 'url': './RangeCalendar/useRangeCalendar'}]; export const description = 'Displays one or more date grids and allows users to select a contiguous range of dates.'; # RangeCalendar diff --git a/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx new file mode 100644 index 00000000000..d5357099345 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx @@ -0,0 +1,159 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/calendar'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a range calendar component.'; +export const isSubpage = true; + +# useRangeCalendar + +{docs.exports.useRangeCalendar.description} + +A range calendar is built from scratch with `useRangeCalendar`, `useCalendarGrid`, and `useCalendarCell`. It shares its grid and cell components with [useCalendar](../Calendar/useCalendar) — only the wrapper differs, using `useRangeCalendarState` and `useRangeCalendar`. The navigation buttons reuse the [Button](../Button) component from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useRangeCalendar, useCalendarGrid, useCalendarCell} from 'react-aria/useRangeCalendar'; +import {useRangeCalendarState} from 'react-stately/useRangeCalendarState'; +import {useLocale} from 'react-aria/I18nProvider'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import {createCalendar} from '@internationalized/date'; +import {Button} from 'vanilla-starter/Button'; +import {ChevronLeft, ChevronRight} from 'lucide-react'; +import 'vanilla-starter/Button.css'; + +function RangeCalendar(props) { + // useRangeCalendarState tracks the selected range and visible date range. + let {locale} = useLocale(); + let state = useRangeCalendarState({...props, locale, createCalendar}); + let ref = React.useRef(null); + let {calendarProps, prevButtonProps, nextButtonProps, title} = useRangeCalendar(props, state, ref); + + return ( +
    +
    + +

    {title}

    + +
    + +
    + ); +} + +function CalendarGrid({state, ...props}) { + // useCalendarGrid renders the table of dates for a single month. + let {gridProps, headerProps, weekDays, weeksInMonth} = useCalendarGrid(props, state); + + return ( + + + + {weekDays.map((day, i) => ( + + ))} + + + + {[...new Array(weeksInMonth).keys()].map(weekIndex => ( + + {state.getDatesInWeek(weekIndex).map((date, i) => ( + date + ? + : + ))} + +
    {day}
    + ))} +
    + ); +} + +function CalendarCell({state, date}) { + // isSelected covers every date within the selected range. + let ref = React.useRef(null); + let {cellProps, buttonProps, isSelected, isOutsideVisibleRange, isDisabled, formattedDate} = + useCalendarCell({date}, state, ref); + + // Show a focus ring when the cell is focused via the keyboard. + let {focusProps, isFocusVisible} = useFocusRing(); + + return ( + + + + ); +} + + +``` + +## Features + +There is no standalone calendar element in HTML. `useRangeCalendar` helps achieve accessible range calendar components that can be styled as needed. + +* Support for selecting a range of dates +* Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more +* Locale-specific formatting, number systems, and right-to-left support +* Date values are provided using objects from the [@internationalized/date](../internationalized/date/) library +* Support for minimum and maximum values, disabled and unavailable dates +* Localized screen reader messages announce the selected and visible date ranges +* Keyboard navigation between dates with arrow keys, including wrapping and validation +* Full keyboard and screen reader accessibility following the [ARIA grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) + +## State management + +`useRangeCalendar` requires a `state` object created with the `useRangeCalendarState` hook from `react-stately`, which holds the selected range and visible date range. Like [useCalendar](../Calendar/useCalendar), it requires a `createCalendar` function from [@internationalized/date](../internationalized/date/) to implement date manipulation across calendar systems. + +## API + + + + + +### AriaRangeCalendarProps + + + +### CalendarAria + + + +### AriaCalendarGridProps + + + +### AriaCalendarCellProps + + From bf9d33799fc3ebe3617add21e967bf5110eb9d2f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:46:23 -0500 Subject: [PATCH 15/56] add useDisclosure docs --- .../s2-docs/pages/react-aria/Disclosure.mdx | 2 +- .../react-aria/Disclosure/useDisclosure.mdx | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Disclosure/useDisclosure.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Disclosure.mdx b/packages/dev/s2-docs/pages/react-aria/Disclosure.mdx index 5ce422d791c..12476230929 100644 --- a/packages/dev/s2-docs/pages/react-aria/Disclosure.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Disclosure.mdx @@ -7,7 +7,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/DisclosureAnatomy.svg'; export const tags = ['accordion', 'collapsible', 'expandable', 'details']; -export const relatedPages = [{'title': 'useDisclosure', 'url': 'Disclosure/useDisclosure.html'}]; +export const relatedPages = [{'title': 'useDisclosure', 'url': './Disclosure/useDisclosure'}]; export const description = 'A collapsible section of content composed of a header with a heading and trigger button, and a panel that contains the content.'; # Disclosure diff --git a/packages/dev/s2-docs/pages/react-aria/Disclosure/useDisclosure.mdx b/packages/dev/s2-docs/pages/react-aria/Disclosure/useDisclosure.mdx new file mode 100644 index 00000000000..93d5b0c6b54 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Disclosure/useDisclosure.mdx @@ -0,0 +1,85 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/disclosure'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a disclosure component.'; +export const isSubpage = true; + +# useDisclosure + +{docs.exports.useDisclosure.description} + +This example builds a disclosure from scratch with `useDisclosure`, reusing the [Button](../Button) component from React Aria Components for the trigger that expands and collapses the panel. + +```tsx render +"use client"; +import React from 'react'; +import {useDisclosure} from 'react-aria/useDisclosure'; +import {useDisclosureState} from 'react-stately/useDisclosureState'; +import {Button} from 'vanilla-starter/Button'; +import {ChevronRight} from 'lucide-react'; +import 'vanilla-starter/Button.css'; + +function Disclosure(props) { + // useDisclosureState tracks whether the panel is expanded. + let state = useDisclosureState(props); + let panelRef = React.useRef(null); + let {buttonProps, panelProps} = useDisclosure(props, state, panelRef); + + return ( +
    +

    + +

    +
    + {props.children} +
    +
    + ); +} + + + Details about system requirements here. + +``` + +## Features + +Disclosures can be built with the HTML [<details>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) and [<summary>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) elements, but these are difficult to style, especially when animating. `useDisclosure` helps achieve accessible disclosures that can be styled as needed. + +* Support for controlled and uncontrolled expanded state +* Exposes the trigger and panel relationship to assistive technology via ARIA +* Keyboard support for toggling the panel with the trigger button +* Can be composed into a disclosure group (accordion) + +## State management + +`useDisclosure` requires a `state` object created with the `useDisclosureState` hook from `react-stately`, which tracks whether the panel is expanded and exposes methods to toggle it. + +## API + + + +### AriaDisclosureProps + + + +### DisclosureAria + + From ad0d6547f71ec8cf2a52eec495b8982a68d833df Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:46:33 -0500 Subject: [PATCH 16/56] add useToolbar docs --- .../dev/s2-docs/pages/react-aria/Toolbar.mdx | 2 +- .../pages/react-aria/Toolbar/useToolbar.mdx | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx b/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx index 9813a2bd341..1ec9689020b 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx @@ -6,7 +6,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/toolbar/toolbar-anatomy.svg'; export const tags = ['group']; -export const relatedPages = [{'title': 'useToolbar', 'url': 'Toolbar/useToolbar.html'}]; +export const relatedPages = [{'title': 'useToolbar', 'url': './Toolbar/useToolbar'}]; export const description = 'A container for a set of interactive controls, such as buttons, dropdown menus, or checkboxes.'; # Toolbar diff --git a/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx b/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx new file mode 100644 index 00000000000..985b7058720 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx @@ -0,0 +1,71 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/toolbar'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a toolbar component.'; +export const isSubpage = true; + +# useToolbar + +{docs.exports.useToolbar.description} + +This example uses `useToolbar` on a container to manage arrow-key navigation between its interactive children, which reuse the [Button](../Button) component from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useToolbar} from 'react-aria/useToolbar'; +import {Button} from 'vanilla-starter/Button'; +import 'vanilla-starter/Button.css'; + +function Toolbar(props) { + // useToolbar manages focus and arrow-key navigation between the children. + let ref = React.useRef(null); + let {toolbarProps} = useToolbar(props, ref); + + return ( +
    + {props.children} +
    + ); +} + + + + + + +``` + +## Features + +There is no native HTML element for a toolbar. `useToolbar` helps achieve an accessible toolbar that groups a set of controls and manages focus between them. + +* Exposed to assistive technology as a `toolbar` via ARIA +* Arrow key navigation between the interactive children, with a single tab stop into the toolbar (roving tabindex) +* Support for horizontal and vertical orientations +* Right-to-left support for arrow key navigation + +## API + + + +### AriaToolbarProps + + + +### ToolbarAria + + From b77774d0ccf82f27757b2c40408bd6108b29cee9 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:46:42 -0500 Subject: [PATCH 17/56] add useToast docs --- .../dev/s2-docs/pages/react-aria/Toast.mdx | 2 +- .../pages/react-aria/Toast/useToast.mdx | 130 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Toast.mdx b/packages/dev/s2-docs/pages/react-aria/Toast.mdx index 117fe6340b7..223de5ec2c3 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toast.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toast.mdx @@ -9,7 +9,7 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['notifications']; export const version = 'alpha'; -export const relatedPages = [{'title': 'useToast', 'url': 'Toast/useToast.html'}]; +export const relatedPages = [{'title': 'useToast', 'url': './Toast/useToast'}]; export const description = 'Displays brief, temporary notifications of actions, errors, or other events in an application.'; # Toast diff --git a/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx b/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx new file mode 100644 index 00000000000..2ab6b806c24 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx @@ -0,0 +1,130 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/toast'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a toast notification.'; +export const isSubpage = true; + +# useToast + +{docs.exports.useToast.description} + +Toasts are built from scratch with `useToastRegion` (the landmark region) and `useToast` (an individual toast), since those hooks are the toast primitives. The trigger and the close button reuse the [Button](../Button) component from React Aria Components. + +```tsx render +"use client"; +import React from 'react'; +import {useToast, useToastRegion} from 'react-aria/useToast'; +import {useToastState} from 'react-stately/useToastState'; +import {Button} from 'vanilla-starter/Button'; +import {X} from 'lucide-react'; +import 'vanilla-starter/Button.css'; + +function ToastProvider({children}) { + // useToastState manages the queue of visible toasts. + let state = useToastState({maxVisibleToasts: 5}); + + return ( + <> + {children(state)} + {state.visibleToasts.length > 0 && } + + ); +} + +function ToastRegion({state, ...props}) { + // useToastRegion creates an ARIA landmark so users can navigate to the toasts. + let ref = React.useRef(null); + let {regionProps} = useToastRegion(props, state, ref); + + return ( +
    + {state.visibleToasts.map(toast => ( + + ))} +
    + ); +} + +function Toast({state, toast, ...props}) { + // useToast provides the props for an individual toast and its close button. + let ref = React.useRef(null); + let {toastProps, contentProps, titleProps, closeButtonProps} = useToast({...props, toast}, state, ref); + + return ( +
    +
    +
    {toast.content}
    +
    + +
    + ); +} + + + {state => ( + + )} + +``` + +## Features + +There is no built in way to display toast notifications in HTML. `useToastRegion` and `useToast` help achieve accessible toasts that can be styled as needed. + +* Toasts display in a landmark region, which keyboard and screen reader users can navigate to +* Support for a queue with a configurable maximum number of visible toasts +* Toasts can be programmatically added and removed, and automatically restore focus when dismissed +* Exposed to assistive technology following the [ARIA alert pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) +* Support for mouse, touch, and keyboard interactions + +## State management + +The toast queue is managed by the `useToastState` hook from `react-stately`, which exposes the list of `visibleToasts` along with `add` and `close` methods. The same state object is passed to `useToastRegion` and `useToast`. A global queue can also be used to trigger toasts from anywhere in an application. + +## API + + + + +### AriaToastRegionProps + + + +### ToastRegionAria + + + +### AriaToastProps + + + +### ToastAria + + From 27ad863ecd7b1c80d4b719df4c28b3e8d2bf4e23 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 12:50:35 -0500 Subject: [PATCH 18/56] add useNumberField docs --- .../s2-docs/pages/react-aria/NumberField.mdx | 2 +- .../react-aria/NumberField/useNumberField.mdx | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/NumberField.mdx b/packages/dev/s2-docs/pages/react-aria/NumberField.mdx index 4979810805e..d69f7efd87c 100644 --- a/packages/dev/s2-docs/pages/react-aria/NumberField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/NumberField.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/numberfield/anatomy.svg'; export const tags = ['input']; -export const relatedPages = [{'title': 'useNumberField', 'url': 'NumberField/useNumberField.html'}]; +export const relatedPages = [{'title': 'useNumberField', 'url': './NumberField/useNumberField'}]; export const description = 'Allows a user to enter a number, and increment or decrement the value using stepper buttons.'; # NumberField diff --git a/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx b/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx new file mode 100644 index 00000000000..e477e296836 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx @@ -0,0 +1,87 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/numberfield'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a number field component.'; +export const isSubpage = true; + +# useNumberField + +{docs.exports.useNumberField.description} + +This example builds a number field from scratch with `useNumberField`, reusing the [Button](../Button) component from React Aria Components for the increment and decrement steppers. + +```tsx render +"use client"; +import React from 'react'; +import {useNumberField} from 'react-aria/useNumberField'; +import {useNumberFieldState} from 'react-stately/useNumberFieldState'; +import {useLocale} from 'react-aria/I18nProvider'; +import {Group} from 'react-aria-components/Group'; +import {Button} from 'vanilla-starter/Button'; +import {Label} from 'vanilla-starter/Form'; +import {Minus, Plus} from 'lucide-react'; +import 'vanilla-starter/NumberField.css'; + +function NumberField(props) { + // useNumberFieldState handles parsing and formatting numbers for the locale. + let {locale} = useLocale(); + let state = useNumberFieldState({...props, locale}); + let inputRef = React.useRef(null); + let {labelProps, groupProps, inputProps, incrementButtonProps, decrementButtonProps} = + useNumberField(props, state, inputRef); + + return ( +
    + + {/* Group renders the focus ring around the whole field via [data-focus-within]. */} + + + + + +
    + ); +} + + +``` + +## Features + +Number fields can be built with ``, but the behavior is very inconsistent across browsers and devices, supports limited localized formatting options, and is challenging to style. `useNumberField` helps achieve accessible number fields that support internationalized formatting and can be styled as needed. + +* Support for internationalized number formatting and parsing, including currencies, percentages, and units +* Support for the arrow keys and scroll wheel to increment and decrement the value +* Support for stepper buttons, with auto-repeat on press and hold +* Validation against minimum and maximum values, and a configurable step +* Support for native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and server-side validation errors +* Exposed to assistive technology as a spinbutton via ARIA + +## State management + +`useNumberField` requires a `state` object created with the `useNumberFieldState` hook from `react-stately`, which manages the numeric value and handles parsing and formatting it according to the user's locale and the provided `formatOptions`. + +## API + + + +### AriaNumberFieldProps + + + +### NumberFieldAria + + From fe4dc45041a3eefecfbb980aa22d853779a76092 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:05:36 -0500 Subject: [PATCH 19/56] add useSeparator docs --- .../s2-docs/pages/react-aria/Separator.mdx | 2 +- .../react-aria/Separator/useSeparator.mdx | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Separator/useSeparator.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Separator.mdx b/packages/dev/s2-docs/pages/react-aria/Separator.mdx index 307cdd108f0..b9a52a4cfad 100644 --- a/packages/dev/s2-docs/pages/react-aria/Separator.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Separator.mdx @@ -8,7 +8,7 @@ import tailwindDocs from 'docs:tailwind-starter/Separator'; import vanillaDocs from 'docs:vanilla-starter/Separator'; import '../../tailwind/tailwind.css'; -export const relatedPages = [{'title': 'useSeparator', 'url': 'Separator/useSeparator.html'}]; +export const relatedPages = [{'title': 'useSeparator', 'url': './Separator/useSeparator'}]; export const description = 'A separator is a visual divider between two groups of content.'; # Separator diff --git a/packages/dev/s2-docs/pages/react-aria/Separator/useSeparator.mdx b/packages/dev/s2-docs/pages/react-aria/Separator/useSeparator.mdx new file mode 100644 index 00000000000..19d75872b38 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Separator/useSeparator.mdx @@ -0,0 +1,78 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/separator'; + +export const section = 'Hooks'; +export const description = 'Provides the accessibility implementation for a separator.'; +export const isSubpage = true; + +# useSeparator + +{docs.exports.useSeparator.description} + +This example uses `useSeparator` to build a visual divider that is correctly exposed to assistive technology, in both horizontal and vertical orientations. + +```tsx render +"use client"; +import React from 'react'; +import {useSeparator} from 'react-aria/useSeparator'; +import 'vanilla-starter/theme.css'; + +function Separator(props) { + let {separatorProps} = useSeparator(props); + + return ( +
    + ); +} + +
    +
    + Content above + + Content below +
    +
    + Content left + + Content right +
    +
    +``` + +## Features + +A separator is a visual divider between two groups of content, e.g. groups of menu items or sections of a page. There is no native HTML element for a separator. `useSeparator` exposes one to assistive technology as a [separator](https://www.w3.org/TR/wai-aria-1.2/#separator) using ARIA. + +* Support for horizontal and vertical orientation +* Exposed to assistive technology via ARIA + +## API + + + +### SeparatorProps + + + +### SeparatorAria + + From 6d098d779a12462550021a344d46821a39db82c6 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:33:36 -0500 Subject: [PATCH 20/56] add useLink docs --- .../dev/s2-docs/pages/react-aria/Link.mdx | 2 +- .../s2-docs/pages/react-aria/Link/useLink.mdx | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Link/useLink.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Link.mdx b/packages/dev/s2-docs/pages/react-aria/Link.mdx index 2388ff085e0..4b87b3f713e 100644 --- a/packages/dev/s2-docs/pages/react-aria/Link.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Link.mdx @@ -8,7 +8,7 @@ import tailwindDocs from 'docs:tailwind-starter/Link'; import '../../tailwind/tailwind.css'; export const tags = ['anchor', 'hyperlink', 'href']; -export const relatedPages = [{'title': 'useLink', 'url': 'Link/useLink.html'}]; +export const relatedPages = [{'title': 'useLink', 'url': './Link/useLink'}]; export const description = 'Allows a user to navigate to another page or resource within a web page or application.'; # Link diff --git a/packages/dev/s2-docs/pages/react-aria/Link/useLink.mdx b/packages/dev/s2-docs/pages/react-aria/Link/useLink.mdx new file mode 100644 index 00000000000..e19cfe2b003 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Link/useLink.mdx @@ -0,0 +1,78 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/link'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a link.'; +export const isSubpage = true; + +# useLink + +{docs.exports.useLink.description} + +This example uses `useLink` to build a navigation link from an `
    ` element, with consistent press behavior across browsers and devices. + +```tsx render +"use client"; +import React from 'react'; +import {useLink} from 'react-aria/useLink'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import 'vanilla-starter/theme.css'; + +function Link(props) { + let ref = React.useRef(null); + let {linkProps} = useLink(props, ref); + // Show a focus ring when focused via the keyboard. + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( + + {props.children} + + ); +} + +Adobe +``` + +## Features + +Links can be created in HTML with the [<a>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) element with an `href` attribute. However, when a link does not navigate to a URL (e.g. a JavaScript handled link), it can be difficult to make it accessible. `useLink` helps achieve accessible links with either native or custom elements that can be styled as needed. + +* Support for mouse, touch, and keyboard interactions +* Support for navigation links via `` elements or custom element types via ARIA +* Support for disabled links + +## API + + + +### AriaLinkOptions + + + +### LinkAria + + From e9ee7ac98a838e2ed06280e3aba4de205faf0801 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:33:47 -0500 Subject: [PATCH 21/56] add useBreadcrumbs docs --- .../s2-docs/pages/react-aria/Breadcrumbs.mdx | 2 +- .../react-aria/Breadcrumbs/useBreadcrumbs.mdx | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx index 9a270196acc..fa40a76d886 100644 --- a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx @@ -7,7 +7,7 @@ import Anatomy from '/packages/react-aria/docs/breadcrumbs/anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['navigation']; -export const relatedPages = [{'title': 'useBreadcrumbs', 'url': 'Breadcrumbs/useBreadcrumbs.html'}]; +export const relatedPages = [{'title': 'useBreadcrumbs', 'url': './Breadcrumbs/useBreadcrumbs'}]; export const description = 'Displays a hierarchy of links to the current page or resource in an application.'; # Breadcrumbs diff --git a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx new file mode 100644 index 00000000000..6a29a6b70e3 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx @@ -0,0 +1,111 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/breadcrumbs'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a breadcrumbs component.'; +export const isSubpage = true; + +# useBreadcrumbs + +{docs.exports.useBreadcrumbs.description} + +This example uses `useBreadcrumbs` for the navigation landmark and `useBreadcrumbItem` for each item, marking the last item as the current page. + +```tsx render +"use client"; +import React from 'react'; +import {useBreadcrumbs, useBreadcrumbItem} from 'react-aria/useBreadcrumbs'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import 'vanilla-starter/theme.css'; + +function Breadcrumbs(props) { + let {navProps} = useBreadcrumbs(props); + let childCount = React.Children.count(props.children); + + return ( + + ); +} + +function BreadcrumbItem(props) { + let ref = React.useRef(null); + let {itemProps} = useBreadcrumbItem({...props, elementType: 'span'}, ref); + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( +
  • + + {props.children} + + {!props.isCurrent && + } +
  • + ); +} + + + alert('Pressed Folder 1')}>Folder 1 + alert('Pressed Folder 2')}>Folder 2 + Folder 3 + +``` + +## Features + +Breadcrumbs provide a list of links to parent pages of the current page in hierarchical order. `useBreadcrumbs` and `useBreadcrumbItem` help implement these in an accessible way. + +* Exposed to assistive technology as a navigation landmark region via ARIA +* The last item is automatically marked as the current page with `aria-current` +* Each item can be a link or a JavaScript handled press, with full keyboard, mouse, and touch support +* Support for disabled items + +## API + + + + +### AriaBreadcrumbsProps + + + +### BreadcrumbsAria + + + +### AriaBreadcrumbItemProps + + + +### BreadcrumbItemAria + + From d8a0ef700189e861a0bf179a04d40dc16c085346 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:33:58 -0500 Subject: [PATCH 22/56] add useMeter docs --- .../dev/s2-docs/pages/react-aria/Meter.mdx | 2 +- .../pages/react-aria/Meter/useMeter.mdx | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Meter.mdx b/packages/dev/s2-docs/pages/react-aria/Meter.mdx index c924ced0d52..b8fdcab3e7c 100644 --- a/packages/dev/s2-docs/pages/react-aria/Meter.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Meter.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/meter/anatomy.svg'; export const tags = ['gauge', 'progress', 'level']; -export const relatedPages = [{'title': 'useMeter', 'url': 'Meter/useMeter.html'}]; +export const relatedPages = [{'title': 'useMeter', 'url': './Meter/useMeter'}]; export const description = 'Represents a quantity within a known range, or a fractional value.'; # Meter diff --git a/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx b/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx new file mode 100644 index 00000000000..4df27f9c316 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx @@ -0,0 +1,77 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/meter'; + +export const section = 'Hooks'; +export const description = 'Provides the accessibility implementation for a meter component.'; +export const isSubpage = true; + +# useMeter + +{docs.exports.useMeter.description} + +This example uses `useMeter` to build a labeled meter that displays a value within a range, with an accessible value label. + +```tsx render +"use client"; +import React from 'react'; +import {useMeter} from 'react-aria/useMeter'; +import 'vanilla-starter/theme.css'; + +function Meter(props) { + let {label, showValueLabel = !!label, value, minValue = 0, maxValue = 100} = props; + let {meterProps, labelProps} = useMeter(props); + + // Calculate the width of the meter bar as a percentage. + let percentage = (value - minValue) / (maxValue - minValue); + let barWidth = `${Math.round(percentage * 100)}%`; + + return ( +
    +
    + {label && {label}} + {showValueLabel && {meterProps['aria-valuetext']}} +
    +
    +
    +
    +
    + ); +} + + +``` + +## Features + +The [<meter>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter) HTML element can be used to build a meter, however it is very difficult to style cross browser. `useMeter` helps achieve accessible meters that can be styled as needed. + +* Exposed to assistive technology as a `meter` via ARIA +* Labeling support for accessibility +* Internationalized number formatting as a percentage or value +* Determinate progress display + +Note: A meter represents a quantity within a known range, such as disk usage. For task progress, use a [progress bar](../ProgressBar/useProgressBar) instead. + +## API + + + +### AriaMeterProps + + + +### MeterAria + + From d104b44669fbfc2602fa0b6c0b1416504c639330 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:34:12 -0500 Subject: [PATCH 23/56] add useProgressBar docs --- .../s2-docs/pages/react-aria/ProgressBar.mdx | 2 +- .../react-aria/ProgressBar/useProgressBar.mdx | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx b/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx index 8e6450fedd9..daadc6f354c 100644 --- a/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx @@ -11,7 +11,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/progress/anatomy.svg'; export const tags = ['loading', 'progress']; -export const relatedPages = [{'title': 'useProgressBar', 'url': 'ProgressBar/useProgressBar.html'}]; +export const relatedPages = [{'title': 'useProgressBar', 'url': './ProgressBar/useProgressBar'}]; export const description = 'Shows either determinate or indeterminate progress of an operation over time.'; # ProgressBar diff --git a/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx b/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx new file mode 100644 index 00000000000..153c0454149 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx @@ -0,0 +1,77 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/progress'; + +export const section = 'Hooks'; +export const description = 'Provides the accessibility implementation for a progress bar component.'; +export const isSubpage = true; + +# useProgressBar + +{docs.exports.useProgressBar.description} + +This example uses `useProgressBar` to build a labeled progress bar that displays the completion progress of a task, with an accessible value label. + +```tsx render +"use client"; +import React from 'react'; +import {useProgressBar} from 'react-aria/useProgressBar'; +import 'vanilla-starter/theme.css'; + +function ProgressBar(props) { + let {label, showValueLabel = !!label, value, minValue = 0, maxValue = 100} = props; + let {progressBarProps, labelProps} = useProgressBar(props); + + // Calculate the width of the progress bar as a percentage. + let percentage = (value - minValue) / (maxValue - minValue); + let barWidth = `${Math.round(percentage * 100)}%`; + + return ( +
    +
    + {label && {label}} + {showValueLabel && {progressBarProps['aria-valuetext']}} +
    +
    +
    +
    +
    + ); +} + + +``` + +## Features + +The [<progress>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) HTML element can be used to build a progress bar, however it is very difficult to style cross browser. `useProgressBar` helps achieve accessible progress bars that can be styled as needed. + +* Exposed to assistive technology as a `progressbar` via ARIA +* Labeling support for accessibility +* Internationalized number formatting as a percentage or value +* Determinate and indeterminate progress support + +Note: A progress bar represents the completion progress of a task. To represent a quantity within a known range, such as disk usage, use a [meter](../Meter/useMeter) instead. + +## API + + + +### AriaProgressBarProps + + + +### ProgressBarAria + + From 5c9782be628db298400c2916fe239ef2d6625c5f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:34:24 -0500 Subject: [PATCH 24/56] add useCheckbox docs --- .../dev/s2-docs/pages/react-aria/Checkbox.mdx | 2 +- .../pages/react-aria/Checkbox/useCheckbox.mdx | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx b/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx index bd49f4fa4c1..db4bc876876 100644 --- a/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/checkbox/checkbox-anatomy.svg'; export const tags = ['input']; -export const relatedPages = [{'title': 'useCheckbox', 'url': 'Checkbox/useCheckbox.html'}]; +export const relatedPages = [{'title': 'useCheckbox', 'url': './Checkbox/useCheckbox'}]; export const description = 'Allows a user to select multiple items from a list of individual items, or to mark one individual item as selected.'; # Checkbox diff --git a/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx b/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx new file mode 100644 index 00000000000..b8363d5ab40 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx @@ -0,0 +1,86 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/checkbox'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a checkbox.'; +export const isSubpage = true; + +# useCheckbox + +{docs.exports.useCheckbox.description} + +This example uses `useCheckbox` to build a checkbox from a native ``, wrapped in a label. + +```tsx render +"use client"; +import React from 'react'; +import {useCheckbox} from 'react-aria/useCheckbox'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import {useToggleState} from 'react-stately/useToggleState'; +import 'vanilla-starter/theme.css'; + +function Checkbox(props) { + // useToggleState tracks the checked state. + let state = useToggleState(props); + let ref = React.useRef(null); + let {inputProps, labelProps} = useCheckbox(props, state, ref); + // Show a focus ring when focused via the keyboard. + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( + + ); +} + +Unsubscribe +``` + +## Features + +Checkboxes can be built with the [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) HTML element, but this can be difficult to style. `useCheckbox` helps achieve accessible checkboxes that can be styled as needed. + +* Built with a native HTML `` element, which is visually hidden when styling custom checkboxes +* Full support for browser features like form autofill +* Keyboard focus management and cross browser normalization +* Labeling support for assistive technology +* Indeterminate state support + +## State management + +`useCheckbox` requires a `state` object created with the `useToggleState` hook from `react-stately`, which tracks whether the checkbox is selected and exposes a method to toggle it. + +## API + + + +### AriaCheckboxProps + + + +### CheckboxAria + + From 1be293197c726019664ab04c9153b8309b2bf848 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:34:34 -0500 Subject: [PATCH 25/56] add useSwitch docs --- .../dev/s2-docs/pages/react-aria/Switch.mdx | 2 +- .../pages/react-aria/Switch/useSwitch.mdx | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Switch.mdx b/packages/dev/s2-docs/pages/react-aria/Switch.mdx index 4d479bc44d1..5582ec487f3 100644 --- a/packages/dev/s2-docs/pages/react-aria/Switch.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Switch.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/switch/anatomy.svg'; export const tags = ['toggle', 'input']; -export const relatedPages = [{'title': 'useSwitch', 'url': 'Switch/useSwitch.html'}]; +export const relatedPages = [{'title': 'useSwitch', 'url': './Switch/useSwitch'}]; export const description = 'Allows a user to turn a setting on or off.'; # Switch diff --git a/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx b/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx new file mode 100644 index 00000000000..64046470b39 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx @@ -0,0 +1,86 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/switch'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a switch.'; +export const isSubpage = true; + +# useSwitch + +{docs.exports.useSwitch.description} + +This example uses `useSwitch` to build a switch with a visually hidden native `` and a custom SVG toggle, including a keyboard focus ring. + +```tsx render +"use client"; +import React from 'react'; +import {useSwitch} from 'react-aria/useSwitch'; +import {VisuallyHidden} from 'react-aria/VisuallyHidden'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {useToggleState} from 'react-stately/useToggleState'; +import 'vanilla-starter/theme.css'; + +function Switch(props) { + let state = useToggleState(props); + let ref = React.useRef(null); + let {inputProps} = useSwitch(props, state, ref); + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( + + ); +} + +Low power mode +``` + +## Features + +Switches can be built with the [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) HTML element, but this can be difficult to style. `useSwitch` helps achieve accessible switches that can be styled as needed. + +* Built with a native HTML `` element, which is visually hidden when styling custom switches +* Full support for browser features like form autofill +* Keyboard focus management and cross browser normalization +* Exposed to assistive technology as a `switch` via ARIA +* Labeling support for assistive technology + +## State management + +`useSwitch` requires a `state` object created with the `useToggleState` hook from `react-stately`, which tracks whether the switch is selected and exposes a method to toggle it. + +## API + + + +### AriaSwitchProps + + + +### SwitchAria + + From 0e10bdf5aefcdd3ec24c38f28a1e57a303064931 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:34:46 -0500 Subject: [PATCH 26/56] add useToggleButton docs --- .../s2-docs/pages/react-aria/ToggleButton.mdx | 2 +- .../ToggleButton/useToggleButton.mdx | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/ToggleButton/useToggleButton.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/ToggleButton.mdx b/packages/dev/s2-docs/pages/react-aria/ToggleButton.mdx index ad6b5cab8a2..cd6e97c03c9 100644 --- a/packages/dev/s2-docs/pages/react-aria/ToggleButton.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ToggleButton.mdx @@ -8,7 +8,7 @@ import tailwindDocs from 'docs:tailwind-starter/ToggleButton'; import '../../tailwind/tailwind.css'; export const tags = ['button', 'btn']; -export const relatedPages = [{'title': 'useToggleButton', 'url': 'ToggleButton/useToggleButton.html'}]; +export const relatedPages = [{'title': 'useToggleButton', 'url': './ToggleButton/useToggleButton'}]; export const description = 'Allows a user to toggle a selection on or off.'; # ToggleButton diff --git a/packages/dev/s2-docs/pages/react-aria/ToggleButton/useToggleButton.mdx b/packages/dev/s2-docs/pages/react-aria/ToggleButton/useToggleButton.mdx new file mode 100644 index 00000000000..192d5b9cbb4 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ToggleButton/useToggleButton.mdx @@ -0,0 +1,87 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/button'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a toggle button.'; +export const isSubpage = true; + +# useToggleButton + +{docs.exports.useToggleButton.description} + +This example uses `useToggleButton` to build a button that toggles between selected and unselected states, reflecting both the press and selection state in its styling. + +```tsx render +"use client"; +import React from 'react'; +import {useToggleButton} from 'react-aria/useToggleButton'; +import {useFocusRing} from 'react-aria/useFocusRing'; +import {mergeProps} from 'react-aria/mergeProps'; +import {useToggleState} from 'react-stately/useToggleState'; +import 'vanilla-starter/theme.css'; + +function ToggleButton(props) { + let ref = React.useRef(null); + let state = useToggleState(props); + let {buttonProps, isPressed} = useToggleButton(props, state, ref); + // Show a focus ring when focused via the keyboard. + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( + + ); +} + +Pin +``` + +## Features + +Toggle buttons are similar to action buttons, but support an additional selection state that is toggled when a user presses the button. There is no native HTML element with toggle button styling, so `useToggleButton` helps achieve accessible toggle buttons that can be styled as needed. + +* Native HTML `
    + ); +} + + + Left + Center + Right + +``` + +## Features + +There is no native HTML element for a group of toggle buttons. `useToggleButtonGroup` and `useToggleButtonGroupItem` help achieve an accessible group of related toggle buttons that can be styled as needed. + +* Exposed to assistive technology as a toolbar or radio group via ARIA, depending on the selection mode +* Support for single or multiple selection +* Arrow key navigation between buttons, with a single tab stop (roving tabindex) +* Support for horizontal and vertical orientations +* Mouse, touch, and keyboard interactions, with cross browser normalization + +## State management + +`useToggleButtonGroup` requires a `state` object created with the `useToggleGroupState` hook from `react-stately`, which tracks the set of selected button ids. The state is passed to each `useToggleButtonGroupItem`, typically via React context. + +## API + + + + +### AriaToggleButtonGroupProps + + + +### ToggleButtonGroupAria + + + +### AriaToggleButtonGroupItemProps + + From cc49fa3312dcb2bba45e052ad0ad8c612732cc50 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 8 Jun 2026 13:49:43 -0500 Subject: [PATCH 30/56] add useTextField docs --- .../s2-docs/pages/react-aria/TextField.mdx | 2 +- .../react-aria/TextField/useTextField.mdx | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/TextField.mdx b/packages/dev/s2-docs/pages/react-aria/TextField.mdx index 2a0b528612a..3cf9c3f4627 100644 --- a/packages/dev/s2-docs/pages/react-aria/TextField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TextField.mdx @@ -9,7 +9,7 @@ import '../../tailwind/tailwind.css'; import Anatomy from '/packages/react-aria/docs/textfield/anatomy.svg'; export const tags = ['input']; -export const relatedPages = [{'title': 'useTextField', 'url': 'TextField/useTextField.html'}]; +export const relatedPages = [{'title': 'useTextField', 'url': './TextField/useTextField'}]; export const description = 'Allows a user to enter a plain text value with a keyboard.'; # TextField diff --git a/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx b/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx new file mode 100644 index 00000000000..2fbc878cac6 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx @@ -0,0 +1,75 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {Layout} from '../../../src/Layout'; +export default Layout; +import {FunctionAPI} from '../../../src/FunctionAPI'; +import {InterfaceType} from '../../../src/types'; +import docs from 'docs:@react-aria/textfield'; + +export const section = 'Hooks'; +export const description = 'Provides the behavior and accessibility implementation for a text field.'; +export const isSubpage = true; + +# useTextField + +{docs.exports.useTextField.description} + +This example builds a text field from scratch with `useTextField`. `useFocus` toggles a `data-focused` attribute so the field shows the focus ring from the reused styles. + +```tsx render +"use client"; +import React from 'react'; +import {useTextField} from 'react-aria/useTextField'; +import {useFocus} from 'react-aria/useFocus'; +import {mergeProps} from 'react-aria/mergeProps'; +import {Label} from 'vanilla-starter/Form'; +import 'vanilla-starter/TextField.css'; + +function TextField(props) { + let ref = React.useRef(null); + let {labelProps, inputProps} = useTextField(props, ref); + let [isFocused, setFocused] = React.useState(false); + let {focusProps} = useFocus({onFocusChange: setFocused}); + + return ( +
    + + +
    + ); +} + + +``` + +## Features + +Text fields can be built with the [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) or [<textarea>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) HTML elements, but those can be difficult to style consistently. `useTextField` helps achieve accessible text fields that can be styled as needed. + +* Support for both `` and `