react-spectrum Documentation

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

15,958 stars TypeScript TypeScript 95 files · ~210,685 tokens #accessibility#design-systems#react#react-components#ui-components#wai-aria
RAW Doc

Technical Documentation: adobe/react-spectrum

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (README.md)

[React Spectrum Libraries](https://react-spectrum.adobe.com/)

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

React Spectrum

A React implementation of Spectrum, Adobe’s design system. Spectrum provides adaptive, accessible, and cohesive experiences for all Adobe applications.

Explore React Spectrum

React Aria

A library of unstyled React components and hooks that helps you build accessible, high quality UI components for your application or design system.

Learn more about React Aria

React Stately

A library of React Hooks that provides cross-platform state management for your design system.

More information about React Stately

Internationalized

A collection of framework-agnostic internationalization libraries for the web.

Internationalized Packages

Features

  • ♿️ Accessible – Accessibility and behavior is implemented according to WAI-ARIA Authoring Practices, including full screen reader and keyboard navigation support. All components have been tested across a wide variety of screen readers and devices to ensure the best experience possible for all users.
  • 📱 Adaptive – All components are designed to work with mouse, touch, and keyboard interactions. They’re built with responsive design principles to deliver a great experience, no matter the device.
  • 🌍 International – Support over 30 languages is included out of the box, including support for right-to-left languages, date and number formatting, and more.
  • 🎨 Customizable – React Spectrum components support custom themes, and automatically adapt for dark mode. For even more customizability, you can build your own components with your own DOM structure and styling using the React Aria and React Stately hooks to provide behavior, accessibility, and interactions.

Getting started

React Spectrum includes several libraries, which you can choose depending on your usecase.

  • React Spectrum is an implementation of Adobe's design system. If you’re integrating with Adobe software or would like a complete component library to use in your project, look no further!
  • React Aria is a collection of unstyled React components and hooks that helps you build accessible, high quality UI components for your own application or design system. If you're building a component library for the web from scratch with your own styling, start here.
  • React Stately is a library of state management hooks for use in your component library. If you're using React Aria, you'll likely also use React Stately, but it can also be used independently (e.g. on other platforms like React Native).

Read more about our architecture.

Contributing

One of the goals of the React Spectrum project is to make building design systems and component libraries as easy as possible, while maintaining high quality interactions and accessibility support. We aim to raise the bar for web applications. The best way to achieve that goal is together. We would love contributions from the community no matter how big or small. 😍

Read our contributing guide to learn about how to propose bugfixes and improvements, and how the development process works. For detailed information about our architecture, and how all of the pieces fit together, read our architecture rfc.


Starters Docs README (`starters/docs/README.md`)

React Aria Starter

Welcome to React Aria! This starter kit includes a Storybook containing all of the examples in the docs. You can modify any of the components or their corresponding CSS files to play around or bootstrap your own component library.

To get started, run the following commands:

shell
yarn
yarn storybook

Building for Production

This starter uses CSS Nesting, which is supported in the latest version of all major browsers, but if further support is needed, you can compile this to flattened selectors by enabling the feature in Lightning CSS or using the PostCSS Nesting plugin in your build.


Packages @Internationalized Date Docs Index (`packages/@internationalized/date/docs/index.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [date, time] order: -1 navigationTitle: Introduction

@internationalized/date

The @internationalized/date package provides objects and functions for representing and manipulating dates and times in a locale-aware manner.

Features

  • Typed objects – Includes immutable objects to represent dates, times, and calendars.
  • International calendars – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more.
  • Manipulation – Add and subtract durations, set and cycle fields, etc.
  • Conversion – Convert between calendar systems, time zones, string representations, and object types.
  • Queries – Compare dates and times for ordering or full/partial equality. Determine locale-specific metadata such as day of week, weekend/weekday, etc.
  • Time zone aware – The ZonedDateTime object supports time zone aware date and time manipulation.
  • Predictable – The API is designed to resolve ambiguity in all operations explicitly, including time zone conversions, arithmetic involving daylight saving time, locale-specific queries, and more.
  • Small bundle size – The entire library including all calendars and functions is 8 kB minified and compressed with Brotli.
  • Tree shakeable – Only include the functions and calendar systems you need. For example, if you only use the Gregorian calendar and builtin CalendarDate methods, it's just 2.8 kB.

Introduction

Dates and times are represented in many different ways by cultures around the world. This includes differences in calendar systems, time zones, daylight saving time rules, date and time formatting, weekday and weekend rules, and much more. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. The @internationalized/date package provides a library of objects and functions to perform date and time related manipulation, queries, and conversions that work across locales and calendars.

By default, JavaScript represents dates and times using the Date object. However, Date has many problems, including a very difficult to use API, lack of all internationalization support, and more. The Temporal proposal will eventually address this in the language, and @internationalized/date is heavily inspired by it. We hope to back the objects in this package with it once it is implemented in browsers.

Package structure

The @internationalized/date package includes the following object types:

  • Calendar – An interface which provides calendar conversion and metadata like number of days in month, and number of months in year. Many implementations are provided to support the most commonly used calendar systems.
  • CalendarDate – An immutable object that stores a date associated with a specific calendar system, without any time components.
  • CalendarDateTime – An immutable object that represents a date and time without a time zone, in a specific calendar system.
  • ZonedDateTime – An immutable object that represents a date and time in a specific time zone and calendar system.
  • Time – An immutable object that stores a clock time without any date components.

Each object includes methods to allow basic manipulation and conversion functionality, such as adding and subtracting durations, and formatting as an ISO 8601 string. Additional less commonly used functions can be imported from the @internationalized/date package, and passed a date object as a parameter. This includes functions to parse ISO 8601 strings, query properties such as day of week, convert between time zones and much more. See the documentation for each of the objects to learn more about the supported methods and functions.

This example constructs a CalendarDate object, manipulates it to get the start of the next week, and converts it to a string representation.

tsx
import {CalendarDate, startOfWeek} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);
date = date.add({weeks: 1});
date = startOfWeek(date, 'en-US');
date.toString(); // 2022-02-06

Packages @Internationalized Number Docs Index (`packages/@internationalized/number/docs/index.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/number/package.json';


category: Numbers keywords: [number, internationalization] order: 0 navigationTitle: Introduction

@internationalized/number

The @internationalized/number package provides utilities for formatting and parsing numbers across locales and numbering systems.

Features

  • Advanced formatting – Formatting and parsing decimals, percentages, currency values, and unit values are supported.
  • Locale-aware – Numbers are formatted and parsed according to the locale expectations, including decimal characters and separators, percent signs, unit and currency symbols, and more.
  • International numbering systems – Support for the Latin, Arabic, and Han decimal numbering systems, including automatic detection of the numbering system used in the input.
  • Small bundle size – The entire library is 1.7 kB minified and compressed with Brotli. No large locale data is needed – all data is retrieved from builtin browser APIs!

Introduction

Numbers are represented in many different ways by cultures around the world. This includes differences in currency symbols, units, decimal points, grouping separators, and even digits themselves. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. The @internationalized/number package provides a utilities to format and parse numbers according to locale expectations.

This example parses a percentage in the Arabic numbering system according to the ar-SA locale, and formats as a Latin number in the en-US locale.

tsx
import {NumberParser, NumberFormatter} from '@internationalized/number';

let parser = new NumberParser('ar-SA', {style: 'percent'});
let number = parser.parse('٤٥٪'); // 0.45

let formatter = new NumberFormatter('en-US', {style: 'percent'});
formatter.format(number); // '45%'

See the docs for NumberParser and NumberFormatter to learn more.


Packages Dev Docs Pages Index (`packages/dev/docs/pages/index.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import {Highlights, Hero} from '@react-spectrum/docs';
import heroNarrow from 'url:./assets/ReactSpectrumHome_Mobile_976x1025_1x.png';
import heroNarrow2x from 'url:./assets/ReactSpectrumHome_Mobile_976x1025_2x.png';
import heroNarrowWebp from 'url:./assets/ReactSpectrumHome_Mobile_976x1025_1x.webp';
import heroNarrow2xWebp from 'url:./assets/ReactSpectrumHome_Mobile_976x1025_2x.webp';
import heroWide from 'url:./assets/ReactSpectrumHome_976x445_1x.png';
import heroWide2x from 'url:./assets/ReactSpectrumHome_976x445_2x.png';
import heroWideWebp from 'url:./assets/ReactSpectrumHome_976x445_1x.webp';
import heroWide2xWebp from 'url:./assets/ReactSpectrumHome_976x445_2x.webp';
import {VisuallyHidden} from '@react-aria/visually-hidden';
import clsx from 'clsx';
import linkStyle from '@adobe/spectrum-css-temp/components/link/vars.css';
import styles from '@react-spectrum/docs/src/docs.css';
import typographyStyles from '@adobe/spectrum-css-temp/components/typography/vars.css';


keywords: [react spectrum, react aria, react stately, design system]

React Spectrum Libraries

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

GitHub

<Highlights items={[
{
title: 'React Spectrum',
url: './react-spectrum/index.html',
urlText: <>Explore React Spectrum</>
},
{
title: 'React Aria',
url: './react-aria/index.html',
urlText: <>Learn more about React Aria</>
},
{
title: 'React Stately',
url: './react-stately/index.html',
urlText: <>Get Started with React Stately</>
},
{
title: 'Internationalized',
url: './internationalized/index.html',
urlText: <>Show Internationalized Packages</>
}
]} />


Packages Dev Docs Pages React Spectrum Getting Started (`packages/dev/docs/pages/react-spectrum/getting-started.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;


category: Introduction

Getting Started

This page describes how to get started building an application with React Spectrum.

What is React Spectrum?

React Spectrum is a React implementation of Spectrum, Adobe's design system.
It provides components that are adaptive to interactions and screen sizes across devices, and includes full
screen reader and keyboard navigation support for accessibility.

Installation

You can install React Spectrum package using a package manager like npm or
yarn.

text
yarn add @adobe/react-spectrum

If you prefer, you can also install components from individually versioned packages. See the versioning
docs for more details.

Build tooling

React Spectrum works with most popular build tooling, like Parcel and
create-react-app, or lower level tools like webpack.

The only requirement is CSS importing support. This is built into Parcel and create-react-app out of the box.
If you're using React Spectrum with raw webpack, then you'll need to configure css-loader.

If your application is written in TypeScript, be sure to add @types/react and @types/react-dom to your project's dependencies as per React's type checking guide. React Spectrum components are published with their type definitions, so you can take advantage of static type checking, IDE autocomplete, and other TypeScript features.

Setting up your app

All React Spectrum applications start with a Provider. The Provider specifies the theme to use,
along with application level settings like the locale. Inside the Provider, you should render your application,
including all React Spectrum components.

tsx example
import {Provider} from '@react-spectrum/provider';
import {theme} from '@react-spectrum/theme-default';
import {Button} from '@react-spectrum/button';

function App() {
  return (
    <Provider theme={theme}>
      <Button
        variant="accent"
        onPress={() => alert('Hey there!')}>
        Hello React Spectrum!
      </Button>
    </Provider>
  );
}

See the Provider and Button docs for more information about the components used in this example.

Next steps

Now that you've set up your app, you can read the documentation for the individual components to understand them in detail.
We also have high level documentation about topics like theming, custom styling,
layout, and testing.


Packages Dev Docs Pages React Spectrum Index (`packages/dev/docs/pages/react-spectrum/index.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import {Highlights, Hero} from '@react-spectrum/docs';
import heroNarrow from 'url:../assets/ReactSpectrum_Mobile_976x1025_1x.png';
import heroNarrow2x from 'url:../assets/ReactSpectrum_Mobile_976x1025_2x.png';
import heroNarrowWebp from 'url:../assets/ReactSpectrum_Mobile_976x1025_1x.webp';
import heroNarrow2xWebp from 'url:../assets/ReactSpectrum_Mobile_976x1025_2x.webp';
import heroWide from 'url:../assets/ReactSpectrum_976x445_1x.png';
import heroWide2x from 'url:../assets/ReactSpectrum_976x445_2x.png';
import heroWideWebp from 'url:../assets/ReactSpectrum_976x445_1x.webp';
import heroWide2xWebp from 'url:../assets/ReactSpectrum_976x445_2x.webp';
import clsx from 'clsx';
import linkStyle from '@adobe/spectrum-css-temp/components/link/vars.css';
import styles from '@react-spectrum/docs/src/docs.css';
import typographyStyles from '@adobe/spectrum-css-temp/components/typography/vars.css';

import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard';
import ComboBoxAnatomy from '../assets/component-illustrations/ComboBox.svg';
import SelectAnatomy from '../assets/component-illustrations/Picker.svg';
import Button from '../assets/component-illustrations/Button.svg';
import ToggleButton from '../assets/component-illustrations/ToggleButton.svg';
import ActionButton from '../assets/component-illustrations/ActionButton.svg';
import ActionGroup from '../assets/component-illustrations/ActionGroup.svg';
import ButtonGroup from '../assets/component-illustrations/ButtonGroup.svg';
import Checkbox from '../assets/component-illustrations/Checkbox.svg';
import CheckboxGroup from '../assets/component-illustrations/CheckboxGroup.svg';
import RadioGroup from '../assets/component-illustrations/RadioGroup.svg';
import Switch from '../assets/component-illustrations/Switch.svg';
import TextField from '../assets/component-illustrations/TextField.svg';
import TextArea from '../assets/component-illustrations/TextArea.svg';
import NumberField from '../assets/component-illustrations/NumberField.svg';
import SearchField from '../assets/component-illustrations/SearchField.svg';
import Form from '../assets/component-illustrations/Form.svg';
import Meter from '../assets/component-illustrations/Meter.svg';
import ProgressCircle from '../assets/component-illustrations/ProgressCircle.svg';
import ProgressBar from '../assets/component-illustrations/ProgressBar.svg';
import StatusLight from '../assets/component-illustrations/StatusLight.svg';
import Tabs from '../assets/component-illustrations/Tabs.svg';
import Link from '../assets/component-illustrations/Link.svg';
import Breadcrumbs from '../assets/component-illustrations/Breadcrumbs.svg';
import Slider from '../assets/component-illustrations/Slider.svg';
import RangeSlider from '../assets/component-illustrations/RangeSlider.svg';
import Dialog from '../assets/component-illustrations/Dialog.svg';
import Tooltip from '../assets/component-illustrations/Tooltip.svg';
import Popover from '../assets/component-illustrations/Popover.svg';
import ContextualHelp from '../assets/component-illustrations/ContextualHelp.svg';
import Menu from '../assets/component-illustrations/Menu.svg';
import ListBox from '../assets/component-illustrations/ListBox.svg';
import ListView from '../assets/component-illustrations/ListView.svg';
import Table from '../assets/component-illustrations/Table.svg';
import Tree from '../assets/component-illustrations/Tree.svg';
import Calendar from '../assets/component-illustrations/Calendar.svg';
import RangeCalendar from '../assets/component-illustrations/RangeCalendar.svg';
import DateField from '../assets/component-illustrations/DateField.svg';
import TimeField from '../assets/component-illustrations/TimeField.svg';
import DatePicker from '../assets/component-illustrations/DatePicker.svg';
import DateRangePicker from '../assets/component-illustrations/DateRangePicker.svg';
import LabeledValue from '../assets/component-illustrations/LabeledValue.svg';
import Badge from '../assets/component-illustrations/Badge.svg';
import TagGroup from '../assets/component-illustrations/TagGroup.svg';
import DropZone from '../assets/component-illustrations/DropZone.svg';
import FileTrigger from '../assets/component-illustrations/FileTrigger.svg'
import ColorSlider from '../assets/component-illustrations/ColorSlider.svg';
import ColorWheel from '../assets/component-illustrations/ColorWheel.svg';
import ColorArea from '../assets/component-illustrations/ColorArea.svg';
import ColorField from '../assets/component-illustrations/ColorField.svg';
import ColorSwatch from '../assets/component-illustrations/ColorSwatch.svg';
import ColorSwatchPicker from '../assets/component-illustrations/ColorSwatchPicker.svg';
import ColorPicker from '../assets/component-illustrations/ColorPicker.svg';
import Disclosure from '../assets/component-illustrations/Disclosure.svg';
import Accordion from '../assets/component-illustrations/DisclosureGroup.svg';
import Toast from '../assets/component-illustrations/Toast.svg';


category: Libraries

React Spectrum

A React implementation of Spectrum, Adobe’s design system.

Get started
•
GitHub

<Highlights items={[
{
title: 'Accessible',
},
{
title: 'Adaptive',
},
{
title: 'Color schemes',
},
{
title: 'International',
}
]} />

Components

Buttons









Pickers





Collections







Class Identifier Description
'gregory' {docs.exports.GregorianCalendar.description}
'buddhist' {docs.exports.BuddhistCalendar.description}
'ethiopic' {docs.exports.EthiopicCalendar.description}
'ethioaa' {docs.exports.EthiopicAmeteAlemCalendar.description}
'coptic' {docs.exports.CopticCalendar.description}
'hebrew' {docs.exports.HebrewCalendar.description}
'indian' {docs.exports.IndianCalendar.description}
'islamic-civil' {renderHTMLfromMarkdown(docs.exports.IslamicCivilCalendar.description)}
'islamic-tbla' {renderHTMLfromMarkdown(docs.exports.IslamicTabularCalendar.description)}
'islamic-umalqura' {renderHTMLfromMarkdown(docs.exports.IslamicUmalquraCalendar.description)}
'japanese' {renderHTMLfromMarkdown(docs.exports.JapaneseCalendar.description)}
'persian' {renderHTMLfromMarkdown(docs.exports.PersianCalendar.description)}
'roc' {renderHTMLfromMarkdown(docs.exports.TaiwanCalendar.description)}

Interface

Custom calendars

You can create your own custom calendar system by implementing the Calendar interface shown above. This enables calendars that follow custom business rules. An example would be a fiscal year calendar that follows a 4-5-4 format, where month ranges don't follow the usual Gregorian calendar.

To implement a calendar, either extend an existing implementation (e.g. GregorianCalendar) or implement the Calendar interface from scratch. The most important methods are fromJulianDay and toJulianDay, which convert between the calendar's year/month/day numbering system and a Julian Day Number. This allows converting dates between calendar systems. Other methods such as getDaysInMonth and getMonthsInYear can be implemented to define how dates are organized in your calendar system.

The following code is an example of how you might implement a custom 4-5-4 calendar (though implementing a true 4-5-4 calendar would be more nuanced than this).

text
/* Detailed source-code truncated for AI context efficiency. */

This enables dates to be converted between calendar systems.

tsx
import {GregorianCalendar, toCalendar} from '@internationalized/date';

let date = new CalendarDate(new Custom454(), 2024, 2, 1);
let gregorianDate = toCalendar(date, new GregorianCalendar());
// => new CalendarDate(new GregorianCalendar(), 2024, 1, 29);

Packages @Internationalized Date Docs CalendarDate (`packages/@internationalized/date/docs/CalendarDate.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/date';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [date, calendar, internationalization]

CalendarDate

{docs.exports.CalendarDate.description}

<HeaderInfo
packageData={packageData}
componentNames={['CalendarDate']}
sourceData={[]} />

Introduction

A CalendarDate object represents a date without any time components, in a specific calendar system such as the Gregorian calendar. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event. If you need to refer to a specific time, use a CalendarDateTime or ZonedDateTime instead.

A CalendarDate can be created using the constructor. This example creates a date that represents February 3rd, 2022 in the Gregorian calendar system.

tsx
import {CalendarDate} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);

You can also create a CalendarDate by parsing an ISO 8601 formatted string using the function.

tsx
import {parseDate} from '@internationalized/date';

let date = parseDate('2022-02-03');

Today's date can be retrieved using the function. This requires a time zone identifier to be provided, which is used to determine the local date. The function can be used to retrieve the user's current time zone.

Note: the local time zone is cached after the first call. You can reset it by calling , or mock it in unit tests by calling .

tsx
import {today, getLocalTimeZone} from '@internationalized/date';

let nyDate = today('America/New_York');
let localDate = today(getLocalTimeZone());

Once you have a CalendarDate object, you can read its properties, or manipulate it as described in the Manipulating dates section below. You can also convert it to an ISO 8601 string, native JavaScript Date object, or another representation. See the Conversion section below for details.

tsx
let date = new CalendarDate(2022, 2, 3);
date.toString(); // '2022-02-03'

Interface

Calendar systems

By default, CalendarDate uses the Gregorian calendar system, but many other calendar systems that are used around the world are supported, such as Hebrew, Indian, Islamic, Buddhist, Ethiopic, and more. A instance can be passed to the CalendarDate constructor to represent dates in that calendar system.

This example creates a date in the Buddhist calendar system, which is equivalent to April 30th, 2020 in the Gregorian calendar.

tsx
import {BuddhistCalendar} from '@internationalized/date';

let date = new CalendarDate(new BuddhistCalendar(), 2563, 4, 30);

See the Calendar docs for details about the supported calendars.

Eras

Many calendar systems have only one era, or a modern era and a pre-modern era (e.g. AD and BC in the Gregorian calendar). However, other calendar systems may have many eras. For example, the Japanese calendar has eras for the reign of each Emperor. CalendarDate represents eras using string identifiers, which can be passed as an additional parameter to the constructor before the year. When eras are present, years are numbered starting from 1 within the era.

This example creates a date in the Japanese calendar system, which is equivalent to April 30th, 2019 in the Gregorian calendar.

tsx
import {JapaneseCalendar} from '@internationalized/date';

let date = new CalendarDate(new JapaneseCalendar(), 'heisei', 31, 4, 30)

A list of valid era identifiers can be retrieved using the getEras method of a Calendar instance. If an era is not provided, the date is assumed to be in the current era.

Converting between calendars

The function can be used to convert a date from one calendar system to another.

This example converts a Gregorian date to a Hebrew one.

tsx
import {toCalendar, HebrewCalendar} from '@internationalized/date';

let gregorianDate = new CalendarDate(2020, 9, 19);
let hebrewDate = toCalendar(gregorianDate, new HebrewCalendar());
// => new CalendarDate(new HebrewCalendar(), 5781, 1, 1)

Manipulating dates

Adding and subtracting durations

A is an object that represents an amount of time, with fields such as years, months, and days. The add and subtract methods of CalendarDate objects can be used to adjust the date by the given duration. These methods return a new date, and do not mutate the original.

tsx
let date = new CalendarDate(2022, 2, 3);

date.add({weeks: 1}); // 2022-02-10
date.add({months: 1}); // 2022-03-03
date.add({years: 1, months: 1, days: 1}); // 2023-03-04

date.subtract({weeks: 1}); // 2022-01-27
date.subtract({months: 1}); // 2022-01-03
date.subtract({years: 1, months: 1, days: 1}); // 2021-01-02

Adding or subtracting a duration that goes beyond the limits of a particular field will cause the date to be balanced. For example, adding one day to August 31st results in September 1st. In addition, if adding or subtracting one field causes another to be invalid, the date will be constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

Parsing durations

The function can be used to convert a ISO 8601 duration string into a object. Negative values can be written by prefixing the entire string with a minus sign.

tsx
parseDuration('P3Y6M6W4D');
// => {years: 3, months: 6, weeks: 6, days: 4}

parseDuration('-P3Y6M6W4D');
// => {years: -3, months: -6, weeks: -6, days: -4}

Setting fields

CalendarDate objects are immutable, which means their properties cannot be set directly. Instead, use the set method, and pass the fields to be modified. This will return a new CalendarDate with the updated values.

tsx
let date = new CalendarDate(2022, 2, 3);
date.set({day: 10}); // 2022-02-10
date.set({month: 5}); // 2022-05-03
date.set({year: 2023, month: 10, day: 16}); // 2023-10-16

Setting a field to a value that is outside the valid range will cause it to be constrained. For example, setting the day to a value that is greater than the number of days in the month, will result in the last day of the month.

tsx
let date = new CalendarDate(2022, 2, 3);
date.set({day: 100}); // 2022-02-28
date.set({month: 20}); // 2022-12-03

Cycling fields

The cycle method allows incrementing or decrementing a single field. It is similar to the add and subtract methods, but when the value reaches the minimum or maximum, it wraps around rather than affecting other fields.

tsx
let date = new CalendarDate(2022, 12, 31);
date.cycle('day', 1); // 2022-12-01
date.cycle('month', 1); // 2022-01-31

let date = new CalendarDate(2022, 1, 1);
date.cycle('day', -1); // 2022-01-31
date.cycle('month', -1); // 2022-12-01

The round option may also be passed, which causes the value to be rounded to increments of the given amount. For example, you could round the year to increments of 5.

tsx
let date = new CalendarDate(2022, 2, 3);

date.cycle('year', 5); // 2027-02-03
date.cycle('year', 5, {round: true}); // 2025-02-03

date.cycle('year', -5); // 2017-02-03
date.cycle('year', -5, {round: true}); // 2020-02-03

Note that if cycling a field causes another field to become invalid, the date is constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

Conversion

To a string

CalendarDate objects can be converted to an ISO 8601 formatted string using the toString method.

tsx
let date = new CalendarDate(2022, 2, 3);
date.toString(); // '2022-02-03'

To a native Date object

A CalendarDate can be converted to a native JavaScript Date object using the toDate method. In general, the Date object should be avoided because it has many internationalization issues and other flaws. However, it is necessary to use some functionality like date formatting.

Because a Date represents an exact time, a time zone identifier is required to be passed to the toDate method. The time of the returned date will be set to midnight in that time zone. The function can be used to retrieve the user's current time zone.

Note: the local time zone is cached after the first call. You can reset it by calling , or mock it in unit tests by calling .

tsx
import {getLocalTimeZone} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);
date.toDate('America/Los_Angeles'); // Thu Feb 03 2022 00:00:00 GMT-0800 (Pacific Standard Time)
date.toDate(getLocalTimeZone()); // e.g. Thu Feb 03 2022 00:00:00 GMT-0500 (Eastern Standard Time)

To a date with time

A CalendarDate can be converted to a CalendarDateTime or ZonedDateTime by providing an optional Time object and/or time zone identifier.

A represents a date with a time, but not in any specific time zone. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.

Use the function to convert a CalendarDate to a CalendarDateTime. By default, the time will be set to midnight. You can also pass a object to set the time to a specific value.

tsx
import {toCalendarDateTime, Time} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);

toCalendarDateTime(date); // 2022-02-03T00:00:00
toCalendarDateTime(date, new Time(8, 30)); // 2022-02-03T08:30:00

A represents a date with a time in a specific time zone. Use this type to represent an exact moment in time at a particular location on Earth.

Use the function to convert a CalendarDate to a ZonedDateTime. The time will be set to midnight in the given time zone. If you need to set a specific time, convert the CalendarDate to a CalendarDateTime first as described above, and pass the result to toZoned.

tsx
import {toZoned, toCalendarDateTime, Time} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);
toZoned(date, 'America/Los_Angeles'); // 2021-02-03T00:00-07:00[America/Los_Angeles]

let dateTime = toCalendarDateTime(date, new Time(8, 30));
toZoned(dateTime, 'America/Los_Angeles'); // 2021-02-03T08:30-07:00[America/Los_Angeles]

Queries

Comparison

CalendarDate objects can be compared either for full or partial equality, or in order to determine which date is before or after another.

The compare method can be used to determine if a date is before or after another. It returns a number less than zero if the first date is before the second, zero if the values are equal, or a number greater than zero if the first date is after the second.

tsx
let a = new CalendarDate(2022, 2, 3);
let b = new CalendarDate(2022, 3, 4);

a.compare(b) < 0; // true
b.compare(a) > 0; // true

In addition, the following functions can be used to perform a partial comparison. These functions accept dates in different calendar systems, and the second date is converted to the calendar system of the first date before comparison.

  • – {docs.exports.isSameYear.description}
  • – {docs.exports.isSameMonth.description}
  • – {docs.exports.isSameDay.description}
  • – {docs.exports.isToday.description}
tsx
import {isSameMonth, IslamicUmalquraCalendar} from '@internationalized/date';

isSameMonth(new CalendarDate(2021, 4, 16), new CalendarDate(2021, 4, 30)); // true
isSameMonth(new CalendarDate(2021, 4, 16), new CalendarDate(2021, 8, 2)); // false
isSameMonth(new CalendarDate(2021, 4, 16), new CalendarDate(new IslamicUmalquraCalendar(), 1442, 9, 4)); // true

A similar set of functions is also available that does not convert between calendar systems and requires the calendars to be equal.

  • – {docs.exports.isEqualYear.description}
  • – {docs.exports.isEqualMonth.description}
  • – {docs.exports.isEqualDay.description}
tsx
import {isEqualMonth, IslamicUmalquraCalendar} from '@internationalized/date';

isEqualMonth(new CalendarDate(2021, 4, 16), new CalendarDate(2021, 4, 30)); // true
isEqualMonth(new CalendarDate(2021, 4, 16), new CalendarDate(2021, 8, 2)); // false
isEqualMonth(new CalendarDate(2021, 4, 16), new CalendarDate(new IslamicUmalquraCalendar(), 1442, 9, 4)); // false

Start and end dates

The following functions can be used to find the start or end dates of a particular unit of time.

  • – {docs.exports.startOfYear.description}
  • – {docs.exports.endOfYear.description}
  • – {docs.exports.startOfMonth.description}
  • – {docs.exports.endOfMonth.description}
  • – {docs.exports.startOfWeek.description}
  • – {docs.exports.endOfWeek.description}

Note that startOfWeek and endOfWeek require a locale string to be provided. This is because the first day of the week changes depending on the locale. For example, in the United States, the first day of the week is on Sunday, but in France it is on Monday.

tsx
import {startOfYear, startOfMonth, startOfWeek} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);

startOfYear(date); // 2022-01-01
startOfMonth(date); // 2022-02-01
startOfWeek(date, 'en-US'); // 2022-01-30
startOfWeek(date, 'fr-FR'); // 2022-01-31

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31

Day of week

The function returns the day of the week for the given date and locale. Days are numbered from zero to six, where zero is the first day of the week in the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getDayOfWeek} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 6); // a Sunday

getDayOfWeek(date, 'en-US'); // 0
getDayOfWeek(date, 'fr-FR'); // 6

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getDayOfWeek(date, 'en-US', 'mon'); // 6

Weekdays and weekends

The and functions can be used to determine if a date is weekday or weekend respectively. This depends on the locale. For example, in the United States, weekends are Saturday and Sunday, but in Israel they are Friday and Saturday.

tsx
import {isWeekday, isWeekend} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 6); // a Sunday

isWeekday(date, 'en-US'); // false
isWeekday(date, 'he-IL'); // true

isWeekend(date, 'en-US'); // true
isWeekend(date, 'he-IL'); // false

Weeks in month

The function returns the number of weeks in the given month. This depends on the number of days in the month, what day of the week the month starts on, and the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getWeeksInMonth} from '@internationalized/date';

let date = new CalendarDate(2021, 1, 1);

getWeeksInMonth(date, 'en-US'); // 6
getWeeksInMonth(date, 'fr-FR'); // 5

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getWeeksInMonth(date, 'en-US', 'mon'); // 5

Packages @Internationalized Date Docs CalendarDateTime (`packages/@internationalized/date/docs/CalendarDateTime.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/date';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [date, time, calendar, internationalization]

CalendarDateTime

{docs.exports.CalendarDateTime.description}

<HeaderInfo
packageData={packageData}
componentNames={['CalendarDateTime']}
sourceData={[]} />

Introduction

A CalendarDateTime object represents a date and time without a time zone, in a specific calendar system such as the Gregorian calendar. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.

A CalendarDateTime can be created using the constructor. This example creates a date that represents February 3rd, 2022 at 9:15 AM in the Gregorian calendar system.

tsx
import {CalendarDateTime} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 15);

You can also create a CalendarDateTime by parsing an ISO 8601 formatted string using the function.

tsx
import {parseDateTime} from '@internationalized/date';

let date = parseDateTime('2022-02-03T09:15');

Once you have a CalendarDateTime object, you can read its properties, or manipulate it as described in the Manipulating dates section below. You can also convert it to an ISO 8601 string, native JavaScript Date object, or another representation. See the Conversion section below for details.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 15);
date.toString(); // '2022-02-03T09:15:00'

Interface

Calendar systems

By default, CalendarDateTime uses the Gregorian calendar system, but many other calendar systems that are used around the world are supported, such as Hebrew, Indian, Islamic, Buddhist, Ethiopic, and more. A instance can be passed to the CalendarDateTime constructor to represent dates in that calendar system.

This example creates a date in the Buddhist calendar system, which is equivalent to April 4th, 2020 at 9:15 AM in the Gregorian calendar.

tsx
import {BuddhistCalendar} from '@internationalized/date';

let date = new CalendarDateTime(new BuddhistCalendar(), 2563, 4, 30, 9, 15);

See the Calendar docs for details about the supported calendars.

Eras

Many calendar systems have only one era, or a modern era and a pre-modern era (e.g. AD and BC in the Gregorian calendar). However, other calendar systems may have many eras. For example, the Japanese calendar has eras for the reign of each Emperor. CalendarDateTime represents eras using string identifiers, which can be passed as an additional parameter to the constructor before the year. When eras are present, years are numbered starting from 1 within the era.

This example creates a date in the Japanese calendar system, which is equivalent to April 4th, 2020 at 9:15 AM in the Gregorian calendar.

tsx
import {JapaneseCalendar} from '@internationalized/date';

let date = new CalendarDateTime(
  new JapaneseCalendar(),
  'heisei', 31, 4, 30, 9, 15
);

A list of valid era identifiers can be retrieved using the getEras method of a Calendar instance. If an era is not provided, the date is assumed to be in the current era.

Converting between calendars

The function can be used to convert a date from one calendar system to another.

This example converts a Gregorian date to a Hebrew one.

tsx
import {toCalendar, HebrewCalendar} from '@internationalized/date';

let gregorianDate = new CalendarDateTime(2020, 9, 19, 10, 30);
let hebrewDate = toCalendar(gregorianDate, new HebrewCalendar());
// => new CalendarDateTime(new HebrewCalendar(), 5781, 1, 1, 10, 30)

Manipulating dates

Adding and subtracting durations

A is an object that represents an amount of time, with fields such as years, months, hours, and minutes. The add and subtract methods of CalendarDateTime objects can be used to adjust the date by the given duration. These methods return a new date, and do not mutate the original.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 45);

date.add({weeks: 1}); // 2022-02-10T09:45
date.add({months: 1}); // 2022-03-03T09:45
date.add({years: 1, months: 1, days: 1}); // 2023-03-04T09:45
date.add({hours: 1}); // 2022-02-03T10:45
date.add({minutes: 30}); // 2022-02-03T10:15

date.subtract({weeks: 1}); // 2022-01-27T09:45
date.subtract({months: 1}); // 2022-01-03T09:45
date.subtract({years: 1, months: 1, days: 1}); // 2021-01-02T09:45
date.subtract({hours: 1}); // 2022-02-03T8:45
date.subtract({minutes: 30}); // 2022-02-03T09:15

Adding or subtracting a duration that goes beyond the limits of a particular field will cause the date to be balanced. For example, adding one day to August 31st results in September 1st. In addition, if adding or subtracting one field causes another to be invalid, the date will be constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

Parsing durations

The function can be used to convert a ISO 8601 duration string into a object. The smallest time unit may include decimal values written with a comma or period, and negative values can be written by prefixing the entire string with a minus sign.

tsx
parseDuration('P3Y6M6W4DT12H30M5S');
// => {years: 3, months: 6, weeks: 6, days: 4, hours: 12, minutes: 30, seconds: 5}

parseDuration('-P3Y6M6W4DT12H30M5S');
// => {years: -3, months: -6, weeks: -6, days: -4, hours: -12, minutes: -30, seconds: -5}

parseDuration('P3Y6M6W4DT12H30M5.5S');
// => {years: 3, months: 6, weeks: 6, days: 4, hours: 12, minutes: 30, seconds: 5.5}

Setting fields

CalendarDateTime objects are immutable, which means their properties cannot be set directly. Instead, use the set method, and pass the fields to be modified. This will return a new CalendarDateTime with the updated values.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 45);
date.set({day: 10}); // 2022-02-10T09:45
date.set({month: 5}); // 2022-05-03T09:45
date.set({year: 2023, month: 10, day: 16}); // 2023-10-16T09:45
date.set({hour: 18}); // 2022-02-03T18:45
date.set({minute: 15}); // 2022-02-03T09:15

Setting a field to a value that is outside the valid range will cause it to be constrained. For example, setting the day to a value that is greater than the number of days in the month, will result in the last day of the month.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 45);
date.set({day: 100}); // 2022-02-28T09:45
date.set({month: 20}); // 2022-12-03T09:45
date.set({hour: 30}); // 2022-02-03T23:45

Cycling fields

The cycle method allows incrementing or decrementing a single field. It is similar to the add and subtract methods, but when the value reaches the minimum or maximum, it wraps around rather than affecting other fields.

tsx
let date = new CalendarDateTime(2022, 12, 31, 23, 59);
date.cycle('day', 1); // 2022-12-01T23:59
date.cycle('month', 1); // 2022-01-31T23:59
date.cycle('hour', 1); // 2022-12-31T00:59
date.cycle('minute', 1); // 2022-12-31T23:00

let date = new CalendarDateTime(2022, 1, 1, 0, 0);
date.cycle('day', -1); // 2022-01-31T00:00
date.cycle('month', -1); // 2022-12-01T00:00
date.cycle('hour', -1); // 2022-01-01T23:00
date.cycle('minute', -1); // 2022-01-01T00:59

Note that if cycling a field causes another field to become invalid, the date is constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

The round option may also be passed, which causes the value to be rounded to increments of the given amount. For example, you could round the minute to increments of 15.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 22);

date.cycle('minute', 15); // 2027-02-03T09:37
date.cycle('minute', 15, {round: true}); // 2025-02-03T09:30

date.cycle('minute', -15); // 2017-02-03T09:07
date.cycle('minute', -15, {round: true}); // 2020-02-03T09:15

By default, the hour field is cycled within a 24 hour range. The hourCycle option can be set to 12 to use a 12 hour clock instead, which will preserve the AM/PM value when formatted.

tsx
let date = new CalendarDateTime(2022, 2, 3, 11);
date.cycle('hour', 1); // 2022-02-03T12:00
date.cycle('hour', 1, {hourCycle: 12}); // 2022-02-03T00:00

let date = new CalendarDateTime(2022, 2, 3, 23);
date.cycle('hour', 1); // 2022-02-03T00:00
date.cycle('hour', 1, {hourCycle: 12}); // 2022-02-03T12:00

Conversion

To a string

CalendarDateTime objects can be converted to an ISO 8601 formatted string using the toString method.

tsx
let date = new CalendarDateTime(2022, 2, 3, 9, 45);
date.toString(); // '2022-02-03T09:45:00'

To a date or time only

A CalendarDateTime can be converted to a CalendarDate or Time object if only one of these components is needed.

Use the function to convert a CalendarDateTime to a CalendarDate.

tsx
import {toCalendarDate} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 45);
toCalendarDate(date); // 2022-02-03

Use the function to convert a CalendarDateTime to a Time.

tsx
import {toTime} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 45);
toTime(date); // 09:45:00

To a ZonedDateTime

A CalendarDateTime can be converted to a local time in a specific time zone using the function. This returns a object, which represents an exact moment in time at a particular location on Earth.

tsx
import {toZoned} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 45);
toZoned(date, 'America/Los_Angeles'); // 2022-02-03T09:45-07:00[America/Los_Angeles]

Some time values during daylight saving transitions may be ambiguous. For example, in the United States, the 2 AM hour is skipped in the spring, and the 1 AM hour occurs twice in the fall. When converting a CalendarDateTime to a ZonedDateTime, this ambiguity must be resolved to determine the exact time. By default, the later of the two possible times is chosen for "spring forward" transitions, and the earlier time is chosen for "fall back" transitions. This can be controlled by passing the disambiguation parameter to the toZoned function.

  • 'earlier' – choose the earlier of the two possible times
  • 'later' – choose the later of the two possible times
  • 'compatible' (default) – choose the later of the two times during "spring forward" transitions, and the earlier time during "fall back" transitions.
  • 'reject' – throws an error when the time is ambiguous
tsx
// A "spring forward" transition
let date = new CalendarDateTime(2020, 3, 8, 2);
toZoned(date, 'America/Los_Angeles'); // 2020-03-08T03:00:00-07:00[America/Los_Angeles]
toZoned(date, 'America/Los_Angeles', 'earlier'); // 2020-03-08T01:00:00-08:00[America/Los_Angeles]
toZoned(date, 'America/Los_Angeles', 'later'); // 2020-03-08T03:00:00-07:00[America/Los_Angeles]

// A "fall back" transition
let date = new CalendarDateTime(2020, 11, 1, 1);
toZoned(date, 'America/Los_Angeles'); // 2020-11-01T01:00:00-07:00[America/Los_Angeles]
toZoned(date, 'America/Los_Angeles', 'earlier'); // 2020-11-01T01:00:00-07:00[America/Los_Angeles]
toZoned(date, 'America/Los_Angeles', 'later'); // 2020-11-01T01:00:00-08:00[America/Los_Angeles]

To a native Date object

A CalendarDateTime can be converted to a native JavaScript Date object using the toDate method. In general, the Date object should be avoided because it has many internationalization issues and other flaws. However, it is necessary to use some functionality like date formatting.

Because a Date represents an exact time, a time zone identifier is required to be passed to the toDate method. The function can be used to retrieve the user's current time zone.

Note: the local time zone is cached after the first call. You can reset it by calling , or mock it in unit tests by calling .

tsx
import {getLocalTimeZone} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 45);
date.toDate('America/Los_Angeles'); // Thu Feb 03 2022 09:45:00 GMT-0800 (Pacific Standard Time)
date.toDate(getLocalTimeZone()); // e.g. Thu Feb 03 2022 09:45:00 GMT-0500 (Eastern Standard Time)

Some time values during daylight saving transitions may be ambiguous. For example, in the United States, the 2 AM hour is skipped in the spring, and the 1 AM hour occurs twice in the fall. When converting a CalendarDateTime to a Date, this ambiguity must be resolved to determine the exact time. This behavior can be controlled using the disambiguation parameter, as described above.

tsx
// A "spring forward" transition
let date = new CalendarDateTime(2020, 3, 8, 2);
date.toDate('America/Los_Angeles', 'earlier');
// => Sun Mar 08 2020 01:00:00 GMT-0800 (Pacific Daylight Time)

Queries

Comparison

CalendarDateTime objects can be compared either for full or partial equality, or in order to determine which date is before or after another.

The compare method can be used to determine if a date is before or after another. It returns a number less than zero if the first date is before the second, zero if the values are equal, or a number greater than zero if the first date is after the second.

tsx
let a = new CalendarDateTime(2022, 2, 3, 9, 45);
let b = new CalendarDateTime(2022, 3, 4, 12, 20);

a.compare(b) < 0; // true
b.compare(a) > 0; // true

In addition, the following functions can be used to perform a partial comparison. These functions accept dates in different calendar systems, and the second date is converted to the calendar system of the first date before comparison.

  • – {docs.exports.isSameYear.description}
  • – {docs.exports.isSameMonth.description}
  • – {docs.exports.isSameDay.description}
  • – {docs.exports.isToday.description}
tsx
import {isSameMonth, IslamicUmalquraCalendar} from '@internationalized/date';

isSameMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(2021, 4, 30, 5, 15)); // true
isSameMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(2021, 8, 2, 5, 15)); // false
isSameMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(new IslamicUmalquraCalendar(), 1442, 9, 4, 5, 15)); // true

A similar set of functions is also available that does not convert between calendar systems and requires the calendars to be equal.

  • – {docs.exports.isEqualYear.description}
  • – {docs.exports.isEqualMonth.description}
  • – {docs.exports.isEqualDay.description}
tsx
import {isEqualMonth, IslamicUmalquraCalendar} from '@internationalized/date';

isEqualMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(2021, 4, 30, 5, 15)); // true
isEqualMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(2021, 8, 2, 5, 15)); // false
isEqualMonth(new CalendarDateTime(2021, 4, 16, 9, 45), new CalendarDateTime(new IslamicUmalquraCalendar(), 1442, 9, 4, 5, 15)); // false

Start and end dates

The following functions can be used to find the start or end dates of a particular unit of time. These only affect the date components of a CalendarDateTime. The time fields are left unchanged.

  • – {docs.exports.startOfYear.description}
  • – {docs.exports.endOfYear.description}
  • – {docs.exports.startOfMonth.description}
  • – {docs.exports.endOfMonth.description}
  • – {docs.exports.startOfWeek.description}
  • – {docs.exports.endOfWeek.description}

Note that startOfWeek and endOfWeek require a locale string to be provided. This is because the first day of the week changes depending on the locale. For example, in the United States, the first day of the week is on Sunday, but in France it is on Monday.

tsx
import {startOfYear, startOfMonth, startOfWeek} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 3, 9, 45);

startOfYear(date); // 2022-01-01T09:45
startOfMonth(date); // 2022-02-01T09:45
startOfWeek(date, 'en-US'); // 2022-01-30T09:45
startOfWeek(date, 'fr-FR'); // 2022-01-31T09:45

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31T09:45

Day of week

The function returns the day of the week for the given date and locale. Days are numbered from zero to six, where zero is the first day of the week in the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getDayOfWeek} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 6, 8, 30); // a Sunday

getDayOfWeek(date, 'en-US'); // 0
getDayOfWeek(date, 'fr-FR'); // 6

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getDayOfWeek(date, 'en-US', 'mon'); // 6

Weekdays and weekends

The and functions can be used to determine if a date is weekday or weekend respectively. This depends on the locale. For example, in the United States, weekends are Saturday and Sunday, but in Israel they are Friday and Saturday.

tsx
import {isWeekday, isWeekend} from '@internationalized/date';

let date = new CalendarDateTime(2022, 2, 6, 8, 30); // a Sunday

isWeekday(date, 'en-US'); // false
isWeekday(date, 'he-IL'); // true

isWeekend(date, 'en-US'); // true
isWeekend(date, 'he-IL'); // false

Weeks in month

The function returns the number of weeks in the given month. This depends on the number of days in the month, what day of the week the month starts on, and the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getWeeksInMonth} from '@internationalized/date';

let date = new CalendarDateTime(2021, 1, 1, 8, 30);

getWeeksInMonth(date, 'en-US'); // 6
getWeeksInMonth(date, 'fr-FR'); // 5

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getWeeksInMonth(date, 'en-US', 'mon'); // 5

Packages @Internationalized Date Docs DateFormatter (`packages/@internationalized/date/docs/DateFormatter.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/date';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [date, internationalization] order: 10

DateFormatter

{docs.exports.DateFormatter.description}

<HeaderInfo
packageData={packageData}
componentNames={['DateFormatter']}
sourceData={[]} />

Introduction

DateFormatter is a wrapper around the native Intl.DateTimeFormat API. It exposes the same API, but works around several browser bugs and provides polyfills for newer features. These are currently:

  • A simple polyfill for the formatRange and formatRangeToParts methods for older browsers.
  • A workaround for a bug involving the hour12 option in Chrome and and the ECMAScript spec.
  • A workaround for a bug involving the hourCycle option in Safari.

See the MDN docs for full details on how to use the API.

Interface


Packages @Internationalized Date Docs Time (`packages/@internationalized/date/docs/Time.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/date';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [time, internationalization]

Time

{docs.exports.Time.description}

<HeaderInfo
packageData={packageData}
componentNames={['Time']}
sourceData={[]} />

Introduction

A Time object represents a clock time without any date components. If you need to refer to a time on a specific date, use a CalendarDateTime or ZonedDateTime instead.

A Time can be created using the constructor. This example creates a time that represents 9:45 AM.

tsx
import {Time} from '@internationalized/date';

let time = new Time(9, 45);

You can also create a Time by parsing an ISO 8601 formatted string using the function.

tsx
import {parseTime} from '@internationalized/date';

let time = parseTime('09:45');

Once you have a Time object, you can read its properties, or manipulate it as described in the Manipulating times section below. You can also convert it to an ISO 8601 string, or another representation. See the Conversion section below for details.

tsx
let time = new Time(9, 45);
time.toString(); // '09:45:00'

Interface

Manipulating times

Adding and subtracting durations

A is an object that represents an amount of time, with fields such as hours, minutes, and seconds. The add and subtract methods of Time objects can be used to adjust the time by the given duration. These methods return a new time, and do not mutate the original.

tsx
let time = new Time(9, 45);
time.add({hours: 1}); // 10:45:00
time.add({minutes: 1}); // 09:46:00
time.add({seconds: 1}); // 09:45:01

Adding or subtracting a duration that goes beyond the limits of a particular field will cause the time to be balanced. For example, adding one minute to 09:59 results in 10:00.

Parsing durations

The function can be used to convert a ISO 8601 duration string into a object. The smallest time unit may include decimal values written with a comma or period, and negative values can be written by prefixing the entire string with a minus sign.

tsx
parseDuration('PT20H35M15S')
// => {hours: 20, minutes: 35, seconds: 15}

parseDuration('-PT20H35M15S')
// =>  {hours: -20, minutes: -35, seconds: -15}

parseDuration('PT20H35M15,75S')
// => {hours: 20, minutes: 35, seconds: 15.75}

Setting fields

Time objects are immutable, which means their properties cannot be set directly. Instead, use the set method, and pass the fields to be modified. This will return a new Time with the updated values.

tsx
let time = new Time(9, 45);
time.set({hour: 12}); // 12:45
time.set({minute: 5}); // 9:05

Setting a field to a value that is outside the valid range will cause it to be constrained.

tsx
let time = new Time(9, 45);
time.set({hour: 36}); // 23:45
time.set({minute: 75}); // 09:59

Cycling fields

The cycle method allows incrementing or decrementing a single field. It is similar to the add and subtract methods, but when the value reaches the minimum or maximum, it wraps around rather than affecting other fields.

tsx
let time = new Time(23, 59);
time.cycle('hour', 1); // 00:59
time.cycle('minute', 1); // 23:00

let time = new Time(0, 0);
time.cycle('hour', -1); // 23:00
time.cycle('minute', -1); // 00:59

The round option may also be passed, which causes the value to be rounded to increments of the given amount. For example, you could round the minute to increments of 15.

tsx
let time = new Time(9, 22);

time.cycle('minute', 15); // 09:37
time.cycle('minute', 15, {round: true}); // 09:30

time.cycle('minute', -15); // 09:07
time.cycle('minute', -15, {round: true}); // 09:15

By default, the hour field is cycled within a 24 hour range. The hourCycle option can be set to 12 to use a 12 hour clock instead, which will preserve the AM/PM value when formatted.

tsx
let time = new Time(11);
time.cycle('hour', 1); // 12:00
time.cycle('hour', 1, {hourCycle: 12}); // 00:00

let time = new Time(23);
time.cycle('hour', 1); // 00:00
time.cycle('hour', 1, {hourCycle: 12}); // 12:00

Conversion

To a string

Time objects can be converted to an ISO 8601 formatted string using the toString method.

tsx
let date = new Time(9, 45);
date.toString(); // '09:45:00'

To a date and time

A Time can be combined with a CalendarDate to produce a CalendarDateTime object using the function.

tsx
import {toCalendarDateTime, CalendarDate} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);
let time = new Time(8, 30);

toCalendarDateTime(date, time); // 2022-02-03T08:30:00

A represents a date with a time, but not in any specific time zone. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a , which represents a date with a time in a specific time zone. Use this type to represent an exact moment in time at a particular location on Earth.

The function can be used to convert a CalendarDateTime to a ZonedDateTime.

tsx
import {toZoned, toCalendarDateTime, CalendarDate} from '@internationalized/date';

let date = new CalendarDate(2022, 2, 3);
let time = new Time(8, 30);

let dateTime = toCalendarDateTime(date, time); // 2022-02-03T08:30:00
toZoned(dateTime, 'America/Los_Angeles'); // 2021-02-03T08:30-07:00[America/Los_Angeles]

Queries

Comparison

Time objects can be compared in order to determine which time is before or after another using the compare method. It returns a number less than zero if the first time is before the second, zero if the values are equal, or a number greater than zero if the first time is after the second.

tsx
let a = new Time(9, 45);
let b = new Time(12, 20);

a.compare(b) < 0; // true
b.compare(a) > 0; // true

Packages @Internationalized Date Docs ZonedDateTime (`packages/@internationalized/date/docs/ZonedDateTime.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/date';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/date/package.json';


category: Date and Time keywords: [date, time, timezone, calendar, internationalization]

ZonedDateTime

{docs.exports.ZonedDateTime.description}

<HeaderInfo
packageData={packageData}
componentNames={['ZonedDateTime']}
sourceData={[]} />

Introduction

A ZonedDateTime object represents an exact date and time in a specific time zone, in a specific calendar system such as the Gregorian calendar. Use this type to represent an exact moment in time at a particular location on Earth.

A ZonedDateTime can be created using the constructor. This example creates a date that represents February 3rd, 2022 at 9:15 AM in the Gregorian calendar system, with a time zone of "America/Los Angeles".

tsx
import {ZonedDateTime} from '@internationalized/date';

let date = new ZonedDateTime(
  // Date
  2022, 2, 3,
  // Time zone and UTC offset
  'America/Los_Angeles', -28800000,
  // Time
  9, 15, 0
);

You can also create a ZonedDateTime by parsing an ISO 8601 formatted string using the one of the following functions:

  • – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. "2021-11-07T00:45[America/Los_Angeles]" or "2021-11-07T00:45-07:00[America/Los_Angeles]"). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.
  • – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. "2021-11-07T07:45:00Z"), or stored with a particular offset (e.g. "2021-11-07T07:45:00-07:00"). A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone.
  • – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for parseAbsolute, and accepts the same formats.
tsx
import {parseZonedDateTime} from '@internationalized/date';

let date = parseZonedDateTime('2022-11-07T00:45[America/Los_Angeles]');
let date = parseAbsolute('2021-11-07T07:45:00Z', 'America/Los_Angeles');
let date = parseAbsoluteToLocal('2021-11-07T07:45:00Z');

You can also create a ZonedDateTime using a Date object or epoch time (milliseconds) using one of the following functions:

  • – This function creates a ZonedDateTime from a Date object. A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone.
  • – This function creates a ZonedDateTime from a Date object. The time zone identifier is automatically resolved from the current user’s environment, and the result is converted into that local time zone.
  • – This function creates a ZonedDateTime from a Unix epoch (e.g. 1688023843144, representing milliseconds since 1970). A time zone identifier, e.g. America/Los_Angeles, must be provided, and the result will be converted into that time zone.
tsx
import {fromDate, fromDateToLocal, fromAbsolute} from '@internationalized/date';

let date = fromDate(new Date(), 'America/Los_Angeles');
let date = fromDateToLocal(new Date());
let date = fromAbsolute(1688023843144, 'America/Los_Angeles');

The current time can be retrieved using the function. This requires a time zone identifier to be provided, which is used to determine the local time. The function can be used to retrieve the user's current time zone.

Note: the local time zone is cached after the first call. You can reset it by calling , or mock it in unit tests by calling .

tsx
import {now, getLocalTimeZone} from '@internationalized/date';

let nyTime = now('America/New_York');
let localTime = now(getLocalTimeZone());

Once you have a ZonedDateTime object, you can read its properties, or manipulate it as described in the Manipulating dates section below. You can also convert it to an ISO 8601 string, native JavaScript Date object, or another representation. See the Conversion section below for details.

tsx
let date = new ZonedDateTime(
  2022, 2, 3,
  'America/Los_Angeles', -28800000,
  12, 24, 45
);

date.toString(); // '2022-02-03T12:24:45-08:00[America/Los_Angeles]'

Interface

Calendar systems

By default, ZonedDateTime uses the Gregorian calendar system, but many other calendar systems that are used around the world are supported, such as Hebrew, Indian, Islamic, Buddhist, Ethiopic, and more. A instance can be passed to the ZonedDateTime constructor to represent dates in that calendar system.

This example creates a date in the Buddhist calendar system, which is equivalent to April 30th, 2020 at 9:15 AM in the Gregorian calendar.

tsx
import {BuddhistCalendar} from '@internationalized/date';

let date = new ZonedDateTime(
  new BuddhistCalendar(), 2563, 4, 30,
  'America/Los_Angeles', -28800000,
  9, 15
);

See the Calendar docs for details about the supported calendars.

Eras

Many calendar systems have only one era, or a modern era and a pre-modern era (e.g. AD and BC in the Gregorian calendar). However, other calendar systems may have many eras. For example, the Japanese calendar has eras for the reign of each Emperor. ZonedDateTime represents eras using string identifiers, which can be passed as an additional parameter to the constructor before the year. When eras are present, years are numbered starting from 1 within the era.

This example creates a date in the Japanese calendar system, which is equivalent to April 30th, 2019 at 9:15 AM in the Gregorian calendar.

tsx
import {JapaneseCalendar} from '@internationalized/date';

let date = new ZonedDateTime(
  new JapaneseCalendar(), 'heisei', 31, 4, 30,
  'America/Los_Angeles', -28800000,
  9, 15
);

A list of valid era identifiers can be retrieved using the getEras method of a Calendar instance. If an era is not provided, the date is assumed to be in the current era.

Converting between calendars

The function can be used to convert a date from one calendar system to another.

This example converts a Gregorian date to a Hebrew one.

tsx
import {toCalendar, HebrewCalendar} from '@internationalized/date';

let gregorianDate = new ZonedDateTime(
  2020, 9, 19,
  'America/Los_Angeles', -28800000,
  10, 30
);

let hebrewDate = toCalendar(gregorianDate, new HebrewCalendar());

Manipulating dates

Adding and subtracting durations

A is an object that represents an amount of time, with fields such as years, months, hours, and minutes. The add and subtract methods of ZonedDateTime objects can be used to adjust the date by the given duration. These methods return a new date, and do not mutate the original.

tsx
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');

date.add({weeks: 1}); // 2022-02-10T09:45[America/Los_Angeles]
date.add({months: 1}); // 2022-03-03T09:45[America/Los_Angeles]
date.add({years: 1, months: 1, days: 1}); // 2023-03-04T09:45[America/Los_Angeles]
date.add({hours: 1}); // 2022-02-03T10:45[America/Los_Angeles]
date.add({minutes: 30}); // 2022-02-03T10:15[America/Los_Angeles]

date.subtract({weeks: 1}); // 2022-01-27T09:45[America/Los_Angeles]
date.subtract({months: 1}); // 2022-01-03T09:45[America/Los_Angeles]
date.subtract({years: 1, months: 1, days: 1}); // 2021-01-02T09:45[America/Los_Angeles]
date.subtract({hours: 1}); // 2022-02-03T8:45[America/Los_Angeles]
date.subtract({minutes: 30}); // 2022-02-03T09:15[America/Los_Angeles]

Adding or subtracting a duration that goes beyond the limits of a particular field will cause the date to be balanced. For example, adding one day to August 31st results in September 1st. In addition, if adding or subtracting one field causes another to be invalid, the date will be constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

Adding and subtracting is time zone aware. When adjusting a date around a daylight saving time transition, the hour may be adjusted accordingly. For example, in the United States, adding one hour during a "spring forward" transition skips the 2 AM hour, and adding an hour in a "fall back" transition repeats the 1 AM hour. Under the hood, the UTC offset is changing instead.

tsx
// A "spring forward" transition
let date = parseZonedDateTime('2020-03-08T01:00-08:00[America/Los_Angeles]');
date.add({hours: 1}); // 2020-03-08T03:00-07:00[America/Los_Angeles]

// A "fall back" transition
let date = parseZonedDateTime('2020-11-01T01:00-07:00[America/Los_Angeles]');
date.add({hours: 1}); // 2020-11-01T01:00-08:00[America/Los_Angeles]
date.add({hours: 2}); // 2020-11-01T02:00-08:00[America/Los_Angeles]

In addition, when changing the date portion of a ZonedDateTime around a daylight saving time transition, the hour may change if it is invalid in the target date. For example, during a "spring forward" transition, the 2 AM hour is skipped.

tsx
// A "spring forward" transition
let date = parseZonedDateTime('2020-03-07T02:00-08:00[America/Los_Angeles]');
date.add({days: 1}); // 2020-03-08T03:00-07:00[America/Los_Angeles]

Parsing durations

The function can be used to convert a ISO 8601 duration string into a object. The smallest time unit may include decimal values written with a comma or period, and negative values can be written by prefixing the entire string with a minus sign.

tsx
parseDuration('P3Y6M6W4DT12H30M5S');
// => {years: 3, months: 6, weeks: 6, days: 4, hours: 12, minutes: 30, seconds: 5}

parseDuration('-P3Y6M6W4DT12H30M5S');
// => {years: -3, months: -6, weeks: -6, days: -4, hours: -12, minutes: -30, seconds: -5}

parseDuration('P3Y6M6W4DT12H30M5.5S');
// => {years: 3, months: 6, weeks: 6, days: 4, hours: 12, minutes: 30, seconds: 5.5}

Setting fields

ZonedDateTime objects are immutable, which means their properties cannot be set directly. Instead, use the set method, and pass the fields to be modified. This will return a new ZonedDateTime with the updated values.

tsx
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
date.set({day: 10}); // 2022-02-10T09:45[America/Los_Angeles]
date.set({month: 5}); // // 2022-05-03T09:45[America/Los_Angeles]
date.set({year: 2023, month: 10, day: 16}); // 2023-10-16T09:45[America/Los_Angeles]
date.set({hour: 18}); // 2022-02-03T18:45[America/Los_Angeles]
date.set({minute: 15}); // 2022-02-03T09:15[America/Los_Angeles]

Setting a field to a value that is outside the valid range will cause it to be constrained. For example, setting the day to a value that is greater than the number of days in the month, will result in the last day of the month.

tsx
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
date.set({day: 100}); // 2022-02-28T09:45[America/Los_Angeles]
date.set({month: 20}); // 2022-12-03T09:45[America/Los_Angeles]
date.set({hour: 30}); // 2022-02-03T23:45[America/Los_Angeles]

Setting fields is time zone aware. When setting fields around a daylight saving time transition, the UTC offset will be adjusted accordingly. For example, when changing the day field from before a daylight saving transition to after, the time remains the same but the UTC offset changes.

tsx
let date = parseZonedDateTime('2020-03-01T10:00-08:00[America/Los_Angeles]');
date.set({day: 14}); // 2020-03-14T10:00-07:00[America/Los_Angeles]

In addition, some time values during daylight saving transitions may be ambiguous. For example, in the United States, the 2 AM hour is skipped in the spring, and the 1 AM hour occurs twice in the fall. When setting date fields, this ambiguity must be resolved to determine the exact time. By default, the later of the two possible times is chosen for "spring forward" transitions, and the earlier time is chosen for "fall back" transitions. This can be controlled by passing the disambiguation parameter to the set method.

  • 'earlier' – choose the earlier of the two possible times
  • 'later' – choose the later of the two possible times
  • 'compatible' (default) – choose the later of the two times during "spring forward" transitions, and the earlier time during "fall back" transitions.
  • 'reject' – throws an error when the time is ambiguous
tsx
// A "spring forward" transition
let date = parseZonedDateTime('2020-03-01T02:00-08:00[America/Los_Angeles]');
date.set({day: 8}); // 2020-03-08T03:00:00-07:00[America/Los_Angeles]
date.set({day: 8}, 'earlier'); // 2020-03-08T01:00:00-08:00[America/Los_Angeles]
date.set({day: 8}, 'later'); // 2020-03-08T03:00:00-07:00[America/Los_Angeles]

// A "fall back" transition
let date = parseZonedDateTime('2020-10-01T01:00-07:00[America/Los_Angeles]');
date.set({month: 11}); // 2020-11-01T01:00:00-07:00[America/Los_Angeles]
date.set({month: 11}, 'earlier'); // 2020-11-01T01:00:00-07:00[America/Los_Angeles]
date.set({month: 11}, 'later'); // 2020-11-01T01:00:00-08:00[America/Los_Angeles]

Cycling fields

The cycle method allows incrementing or decrementing a single field. It is similar to the add and subtract methods, but when the value reaches the minimum or maximum, it wraps around rather than affecting other fields.

tsx
let date = parseZonedDateTime('2022-12-31T23:59[America/Los_Angeles]');
date.cycle('day', 1); // 2022-12-01T23:59[America/Los_Angeles]
date.cycle('month', 1); // 2022-01-31T23:59[America/Los_Angeles]
date.cycle('hour', 1); // 2022-12-31T00:59[America/Los_Angeles]
date.cycle('minute', 1); // 2022-12-31T23:00[America/Los_Angeles]

let date = parseZonedDateTime('2022-01-01T00:00[America/Los_Angeles]');
date.cycle('day', -1); // 2022-01-31T00:00[America/Los_Angeles]
date.cycle('month', -1); // 2022-12-01T00:00[America/Los_Angeles]
date.cycle('hour', -1); // 2022-01-01T23:00[America/Los_Angeles]
date.cycle('minute', -1); // 2022-01-01T00:59[America/Los_Angeles]

Note that if cycling a field causes another field to become invalid, the date is constrained. For example, adding one month to August 31st results in September 30th because September 31st does not exist.

The round option may also be passed, which causes the value to be rounded to increments of the given amount. For example, you could round the minute to increments of 15.

tsx
let date = parseZonedDateTime('2022-02-03T09:22[America/Los_Angeles]');

date.cycle('minute', 15); // 2022-02-03T09:37:00-08:00[America/Los_Angeles]
date.cycle('minute', 15, {round: true}); // 2022-02-03T09:30:00-08:00[America/Los_Angeles]

date.cycle('minute', -15); // 2022-02-03T09:07:00-08:00[America/Los_Angeles]
date.cycle('minute', -15, {round: true}); // 2022-02-03T09:15:00-08:00[America/Los_Angeles]

By default, the hour field is cycled within a 24 hour range. The hourCycle option can be set to 12 to use a 12 hour clock instead, which will preserve the AM/PM value when formatted.

tsx
let date = parseZonedDateTime('2022-02-03T11:00[America/Los_Angeles]');
date.cycle('hour', 1); // 2022-02-03T12:00[America/Los_Angeles]
date.cycle('hour', 1, {hourCycle: 12}); // 2022-02-03T00:00[America/Los_Angeles]

let date = parseZonedDateTime('2022-02-03T23:00[America/Los_Angeles]');
date.cycle('hour', 1); // 2022-02-03T00:00[America/Los_Angeles]
date.cycle('hour', 1, {hourCycle: 12}); // 2022-02-03T12:00[America/Los_Angeles]

Cycling fields is time zone aware. When adjusting a date around a daylight saving time transition, the hour may be adjusted accordingly. For example, in the United States, adding one hour during a "spring forward" transition skips the 2 AM hour, and adding an hour in a "fall back" transition repeats the 1 AM hour. Under the hood, the UTC offset is changing instead.

tsx
// A "spring forward" transition
let date = parseZonedDateTime('2020-03-08T01:00-08:00[America/Los_Angeles]');
date.cycle('hour', 1); // 2020-03-08T03:00-07:00[America/Los_Angeles]

// A "fall back" transition
let date = parseZonedDateTime('2020-11-01T01:00-07:00[America/Los_Angeles]');
date.cycle('hour', 1); // 2020-11-01T01:00-08:00[America/Los_Angeles]

In addition, when changing the date portion of a ZonedDateTime around a daylight saving time transition, the hour may change if it is invalid in the target date. For example, during a "spring forward" transition, the 2 AM hour is skipped.

tsx
// A "spring forward" transition
let date = parseZonedDateTime('2020-03-07T02:00-08:00[America/Los_Angeles]');
date.cycle('day', 1); // 2020-03-08T03:00-07:00[America/Los_Angeles]

Conversion

To a string

ZonedDateTime objects can be converted to an ISO 8601 formatted string using the toString method. This format preserves the maximum amount of information, including the UTC offset and time zone identifier, in the same format used in other languages like Java.

tsx
let date = new ZonedDateTime(
  2022, 2, 3,
  'America/Los_Angeles', -28800000,
  12, 24, 45
);

date.toString(); // '2022-02-03T12:24:45-08:00[America/Los_Angeles]'

If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.

You can also use the toAbsoluteString method to convert the date to a UTC string. Use this format if the exact time no matter the time zone is needed.

tsx
date.toAbsoluteString(); // '2022-02-03T20:24:45.000Z'

To a native Date object

A ZonedDateTime can be converted to a native JavaScript Date object using the toDate method. In general, the Date object should be avoided because it has many internationalization issues and other flaws. However, it is necessary to use some functionality like date formatting.

tsx
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
date.toDate('America/Los_Angeles'); // Thu Feb 03 2022 09:45:00 GMT-0800 (Pacific Standard Time)

To a date or time only

A ZonedDateTime can be converted to a CalendarDate or Time object if only one of these components is needed.

Use the function to convert a ZonedDateTime to a CalendarDate.

tsx
import {toCalendarDate} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toCalendarDate(date); // 2022-02-03

Use the function to convert a ZonedDateTime to a Time.

tsx
import {toTime} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toTime(date); // 09:45

To a date and time without a time zone

A ZonedDateTime can be converted to a CalendarDateTime, which represents a date and time that is not in any specific time zone. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.

Use the function to convert a ZonedDateTime to a CalendarDateTime.

tsx
import {toCalendarDateTime} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toCalendarDateTime(date); // 2022-02-03T09:45:00

Between time zones

A ZonedDateTime can be converted to a different time zone using the function. The function can also be used to convert to the current user's local time zone.

tsx
import {toTimeZone, toLocalTimeZone} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toTimeZone(date, 'America/Chicago'); // 2022-02-03T11:45[America/Chicago]
toLocalTimeZone(date); // e.g. 2022-02-03T12:45[America/New_York]

Queries

Comparison

ZonedDateTime objects can be compared either for full or partial equality, or in order to determine which date is before or after another.

The compare method can be used to determine if a date is before or after another. It returns a number less than zero if the first date is before the second, zero if the values are equal, or a number greater than zero if the first date is after the second.

tsx
let a = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
let b = parseZonedDateTime('2022-03-04T09:45[America/Los_Angeles]');

a.compare(b) < 0; // true
b.compare(a) > 0; // true

In addition, the following functions can be used to perform a partial comparison. These functions accept dates in different calendar systems, and the second date is converted to the calendar system of the first date before comparison.

  • – {docs.exports.isSameYear.description}
  • – {docs.exports.isSameMonth.description}
  • – {docs.exports.isSameDay.description}
  • – {docs.exports.isToday.description}
tsx
import {isSameMonth} from '@internationalized/date';

isSameMonth(
  parseZonedDateTime('2022-04-16T09:45[America/Los_Angeles]'),
  parseZonedDateTime('2022-04-30T05:15[America/Los_Angeles]'),
); // true

isSameMonth(
  parseZonedDateTime('2022-04-16T09:45[America/Los_Angeles]'),
  parseZonedDateTime('2022-08-020T05:15[America/Los_Angeles]'),
); // false

A similar set of functions is also available that does not convert between calendar systems and requires the calendars to be equal.

  • – {docs.exports.isEqualYear.description}
  • – {docs.exports.isEqualMonth.description}
  • – {docs.exports.isEqualDay.description}

Start and end dates

The following functions can be used to find the start or end dates of a particular unit of time. These only affect the date components of a ZonedDateTime. The time fields are left unchanged.

  • – {docs.exports.startOfYear.description}
  • – {docs.exports.endOfYear.description}
  • – {docs.exports.startOfMonth.description}
  • – {docs.exports.endOfMonth.description}
  • – {docs.exports.startOfWeek.description}
  • – {docs.exports.endOfWeek.description}

Note that startOfWeek and endOfWeek require a locale string to be provided. This is because the first day of the week changes depending on the locale. For example, in the United States, the first day of the week is on Sunday, but in France it is on Monday.

tsx
import {startOfYear, startOfMonth, startOfWeek} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');

startOfYear(date); // 2022-01-01T09:45[America/Los_Angeles]
startOfMonth(date); // 2022-02-01T09:45[America/Los_Angeles]
startOfWeek(date, 'en-US'); // 2022-01-30T09:45[America/Los_Angeles]
startOfWeek(date, 'fr-FR'); // 2022-01-31T09:45[America/Los_Angeles]

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31T09:45[America/Los_Angeles]

Day of week

The function returns the day of the week for the given date and locale. Days are numbered from zero to six, where zero is the first day of the week in the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getDayOfWeek} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-06T08:30[America/Los_Angeles]'); // a Sunday

getDayOfWeek(date, 'en-US'); // 0
getDayOfWeek(locale, 'fr-FR'); // 6

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getDayOfWeek(date, 'en-US', 'mon'); // 6

Weekdays and weekends

The and functions can be used to determine if a date is weekday or weekend respectively. This depends on the locale. For example, in the United States, weekends are Saturday and Sunday, but in Israel they are Friday and Saturday.

tsx
import {isWeekday, isWeekend} from '@internationalized/date';

let date = parseZonedDateTime('2022-02-06T08:30[America/Los_Angeles]'); // a Sunday

isWeekday(date, 'en-US'); // false
isWeekday(date, 'he-IL'); // true

isWeekend(date, 'en-US'); // true
isWeekend(date, 'he-IL'); // false

Weeks in month

The function returns the number of weeks in the given month. This depends on the number of days in the month, what day of the week the month starts on, and the given locale. For example, in the United States, the first day of the week is Sunday, but in France it is Monday.

tsx
import {getWeeksInMonth} from '@internationalized/date';

let date = parseZonedDateTime('2023-01-01T08:30[America/Los_Angeles]');

getWeeksInMonth(date, 'en-US'); // 5
getWeeksInMonth(date, 'fr-FR'); // 6

You can also provide an optional firstDayOfWeek argument to override the default first day of the week determined by the locale. It accepts a week day abbreviation, e.g. sun, mon, tue, etc.

tsx
getWeeksInMonth(date, 'en-US', 'mon'); // 6

Packages @Internationalized Number Docs NumberFormatter (`packages/@internationalized/number/docs/NumberFormatter.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/number';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/number/package.json';


category: Numbers keywords: [number, internationalization] order: 2

NumberFormatter

{docs.exports.NumberFormatter.description}

<HeaderInfo
packageData={packageData}
componentNames={['NumberFormatter']}
sourceData={[]} />

Introduction

NumberFormatter is a wrapper around the native Intl.NumberFormat API. It exposes the same API, but works around several browser bugs.

See the MDN docs for full details on how to use the API.

Interface


Packages @Internationalized Number Docs NumberParser (`packages/@internationalized/number/docs/NumberParser.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@internationalized/number';
import {HeaderInfo, FunctionAPI, ClassAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@internationalized/number/package.json';


category: Numbers keywords: [number, parser, internationalization] order: 1

NumberParser

{docs.exports.NumberParser.description}

<HeaderInfo
packageData={packageData}
componentNames={['NumberParser']}
sourceData={[]} />

Introduction

Numbers can be formatted in many different ways, including percentages, units, decimals, currencies, and more. In addition, number formatting varies around the world. For example, currency symbols, units, decimal separators, and even digits themselves may be different across languages and regions. While Latin numerals are the most commonly used, many other numbering systems are also used around the world. For example, in the Latin numbering system, the number twelve is represented as “12”, and in the Arabic decimal system, it is “١٢”.

NumberParser is designed to validate and parse numbers from user input according to a specific locale and format. It supports several different numbering systems including the Latin, Arabic, and Han positional numbering systems, as well as parsing decimals, percentages, currencies, and unit values. The numbering system is automatically detected from the input string. This means that users may input numbers in a different numbering system than the default for their locale, e.g. a Latin number in an Arabic locale.

Parsing numbers while taking into account all locale-specific detail is quite complex and error-prone. NumberParser uses information about the locale and expected format for a number in order to parse it correctly. This means it is somewhat strict about the accepted formats. It is not designed to handle use cases where the user can enter numbers in an unknown format (e.g. either a unit value or a percentage), this must be known up front or selected via an external UI control.

Read our blog post for more details on how the number parser is implemented.

Example

To create a NumberParser, call the constructor with a locale string and optional format options. The same options as supported by the Intl.NumberFormat object are supported. See the docs on MDN linked above for full details.

This example creates a parser than accepts values in inches in the en-US locale.

tsx
import {NumberParser} from '@internationalized/number';

let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.parse('10 in'); // 10

Interface

Parsing

Numbers can be parsed using the parse method. If the input is not a valid number according to the locale and format options, NaN is returned instead.

Decimals

By default, or with the style: 'decimal' option, NumberParser supports parsing decimal values.

tsx
let parser = new NumberParser('en-US');
parser.parse('10.5'); // 10.5
parser.parse('-25.6'); // -25.6
parser.parse('1,000,000'); // 1000000
parser.parse('١٢'); // 12
parser.parse('X'); // NaN

Percentages

The style: 'percent' option can be provided to the constructor to parse percentage values. In this mode, parsed values are divided by 100, for example the string "45%" is parsed as 0.45. The maximumFractionDigits option is also taken into account during parsing, and the resulting value is rounded accordingly.

tsx
let parser = new NumberParser('en-US', {
  style: 'percent',
  maximumFractionDigits: 2
});

parser.parse('45%'); // 0.45
parser.parse('62.3333%'); // 0.6233

Currency values

The style: 'currency' option can be provided to the constructor to parse currency values. The currency option must also be passed to set the currency code (e.g. USD) to use. In addition, the currencyDisplay option can be used to choose whether to display the currency symbol, currency code, or currency name. Finally, the currencySign option can be set to accounting to use accounting notation for negative numbers, which uses parentheses rather than a minus sign in some locales.

Note that partial input is not accepted by this method. For example, if a partial currency symbol is included in the string, NaN will be returned. The isValidPartialNumber method, described below, can be used in scenarios where you have partial user input.

tsx
let parser = new NumberParser('en-US', {
  style: 'currency',
  currency: 'EUR',
  currencyDisplay: 'code',
  currencySign: 'accounting'
});

parser.parse('45'); // 45
parser.parse('EUR 45'); // 45
parser.parse('EUR 26.45'); // 26.45
parser.parse('EUR -25'); // -25
parser.parse('(EUR 25)'); // -25
parser.parse('EU 45'); // NaN (partial currency symbol)
parser.parse('$45'); // NaN (different currency symbol)

Unit values

The style: 'unit' option can be passed to the constructor to parse values with a unit of measurement. The unit option must also be passed to set which unit to use (e.g. inch). In addition, the unitDisplay option can be used to choose whether the unit is accepted in long, short, or narrow format.

tsx
let parser = new NumberParser('en-US', {
  style: 'unit',
  unit: 'inch',
  unitDisplay: 'long'
});

parser.parse('12'); // 12
parser.parse('12 inches'); // 12
parser.parse('1 inch'); // 1
parser.parse('12 in'); // NaN (partial unit)
parser.parse('23 ft'); // NaN (different unit)

Validation

NumberParser can also be used to validate partial user input using the isValidPartialNumber method, for example, as the user types into an input field. The parse method only accepts complete input, whereas isValidPartialNumber determines if the given input might be valid but incomplete. For example, only entering a decimal point is invalid when passed to parse, but accepted by isValidPartialNumber.

Note that partial units and currency symbols are not accepted. Since the unit itself must be known upfront and passed to the constructor, it is better to only allow entering a number rather and autocompleting the unit rather than typing it in one letter at a time.

tsx
let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.isValidPartialNumber('.'); // true
parser.isValidPartialNumber('.2'); // true
parser.isValidPartialNumber('10 in'); // true
parser.isValidPartialNumber('10 i'); // false
parser.isValidPartialNumber('10 x'); // false

Detecting the numbering system

Under the hood, NumberParser automatically detects the numbering system used in the input. This is also exposed by the getNumberingSystem method, which returns a Unicode script identifier. For example, this can be used to create a number formatter based on the numbering system used in the input.

tsx
import {NumberParser, NumberFormatter} from '@internationalized/number';

let parser = new NumberParser('en-US', {style: 'decimal'});
let numberingSystem = parser.getNumberingSystem('١٢'); // -> 'arab'

let formatter = new NumberFormatter('en-US', {style: 'decimal', numberingSystem});
formatter.format(12); // '١٢'

Packages Dev Docs Pages Architecture (`packages/dev/docs/pages/architecture.mdx`)

{/* Copyright 2020 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 {Flex} from '@react-spectrum/layout';
import {View} from '@react-spectrum/view';
import style from '@react-spectrum/docs/src/docs.css';
import {Layout} from '@react-spectrum/docs';
export default Layout;


category: Foundation keywords: [react stately, react aria, react spectrum, hooks, react, spectrum]

Architecture

This page discusses the architecture of React Spectrum, and how
behavior and core logic for many common components can be shared
between design systems and across platforms.

Introduction

Today, many companies are implementing their own component libraries for their
design systems from scratch. On the web, the primitives are very low level.
There are very few built-in browser controls that are fully styleable, so
each company needs to reimplement many of the same components from scratch.
This has become easier to do with React and other modern view libraries, but
implementing full support for accessibility, internationalization, keyboard,
mouse, and touch interactions, and other features is still extraordinarily
difficult.

In addition, JavaScript has spread beyond only the web platform. Platforms
like React Native allow React to render native widgets across mobile
and desktop operating systems as well. This will continue to expand in the
future as new platforms and interaction models are developed.

While each design system is unique, there is often more in common between
components than different. Most components typically found in a design
system, like buttons, checkboxes, selects, and even
tables, usually have very similar behavior and logic. There are even
specifications that describe how many of the most common components should
behave in terms of accessibility semantics
and keyboard interactions.
The main difference between design systems is the styling.

This opens up to the possibility of sharing much of the behavior and
component logic between design systems and across platforms. For example,
user interactions, accessibility, internationalization, and behavior
can be reused, while allowing custom styling and rendering to live
within individual design systems. This has the potential to improve
the overall quality of applications, while saving companies money
and time, and reducing duplicated effort across the industry.

Architecture

Component Behavior Hook State Hook

In order to allow reusing component behavior between design systems,
React Spectrum splits each component into three parts: state, behavior,
and the rendered component.

Some components don't have all of these pieces. For example, some simple
components do not require any state, and others are only compositions of
other components, so each component has the pieces of this architecture
that make sense for their purpose.

This architecture is made possible by
React Hooks, which enable
the ability to reuse behavior between multiple components. Hooks allow
accessing React features like state and effects from functions which
can be called from any component. If you're unfamiliar with hooks,
we recommend reading the documentation linked above first.

State hook

At the bottom is the state hook. This hook is shared across platforms —
it could work on the web, react-native, or any other platform, and makes
no assumptions about the view system it is running on. It also has no
theme or design system specific logic.

The state hook accepts common props from the component and provides
state management. It implements the core logic for the component and
returns an interface for reading and updating the component state.

Not all components have a state hook. For example, many components are
read-only — they display something to the user but don't allow them
to change it. State hooks are found in interactive components that allow
data entry, or have some kind of visual state that the user can update
(e.g. expanding/collapsing).

Behavior hook

In the middle is the behavior hook. This hook is platform specific, and
depends on the platform API (e.g. the DOM or react-native). It also has
no theme or design system specific logic. It implements event
handling, accessibility, internationalization, etc. — all the parts of a
component that could be shared across multiple design systems.

The behavior hook uses the state hook in order to implement component behavior.
It returns one or more sets of platform specific props (e.g. DOM props) that
can be spread onto the elements rendered by the component. These include
semantic properties like ARIA, and
event handlers. The event handlers in turn call methods on
the state interface to implement the behavior for the component.

Some components may not have any user interactions, but still have a behavior
hook. Most components have some kind of semantics that they need to expose
for accessibility (a form of behavior). The only components that won't have
a behavior hook of their own are those that only compose together other
components.

Component

At the top is the component, which lives in each design system and composes
all of these pieces together. It provides the theme and design system specific
logic, and renders the actual platform elements. It applies styles, which could
be implemented in many different ways (e.g. CSS classes, CSS-in-JS, etc.).

The component uses props returned by the behavior hook and state from the state
hook to implement the visual appearance. It spreads props returned from the behavior
hook onto elements that it renders to apply semantics and interactions, and can use
the state from the state hook to adjust its visual appearance.
This allows complete control over the rendering of the component, including
things like adding extra elements as needed for styling or layout control.

The component itself may be split into unstyled and styled parts. Unstyled components
compose together hooks and abstract away the glue code necessary to connect them together
by providing reusable contexts, DOM structure, and styling APIs. This simplifies design system
components while retaining the flexibility to drop down to lower-level APIs when needed.

Implementation

React Spectrum React Aria React Stately

Our implementation of this architecture splits each piece into three npm
scopes, with individually versioned packages for each component. This
allows you incrementally adopt this approach in your own design system
and reuse as much or as little as makes sense for your usecases.

Within a component, the hooks are designed to be highly composable,
with individual features split into many hooks. This allows you to combine
them together and achieve your design with only the features you need in
your design system.

The overall goal for the project is to make reusing behavior across design
systems as easy as possible, while allowing full design customizability
and avoiding code bloat.

React Stately

React Stately implements state management and
core logic for each component. It handles complex logic for things
like collections and selection
in a fully cross-platform way that you could reuse on the web, in
react-native, etc.

React Stately hooks can be used independently in your own components,
or paired with React Aria hooks to get more of the behavior and user
interactions for web applications out of the box. We do not yet have
behavior hooks for other platforms however, so if you're working in
react-native or another view system, you'll need to use React Stately
directly.

Read more about React Stately and get started
by reading the docs.

React Aria

React Aria implements behavior and accessibility
for the web according to the W3C's ARIA Authoring Practices Guide.
It includes full screen reader and keyboard navigation support, along with
mouse and touch interactions that have been
tested across a wide variety of devices and browsers. It also implements
internationalization for over 30
languages, including right-to-left specific behavior, localized date and
number formatting, and more.

Most importantly, React Aria is fully customizable. It offers both high-level
unstyled components and low-level hooks depending on the level of customization you need.
The components provide a default DOM structure and styling API, and handle all
of the glue code necessary to connect the hooks together on your behalf.
If you need even more control, you can drop down to the lower-level hook-based API,
which enables advanced customization use cases such
as overriding DOM elements, intercepting events, customizing behavior, and more.

Read more about React Aria and
the problems it solves, and check out the docs to get started
building your own design system.

React Spectrum

React Spectrum puts all of these pieces together
and implements the Adobe-specific styling. It's designed to be adaptive, and
works across mouse, touch, and keyboard interactions, on devices of any screen
size. It supports theming, including automatic
support for dark mode and responsive scaling for large hit targets on touch
devices.

If you're integrating with Adobe software, React Spectrum is a great way to
make your UI consistent. It's also a really great example of how to use
React Aria and React Stately to build a full design system, so if you're
developing your own component library, you can check out the
source code for
inspiration.

Get started building an application
with React Spectrum, and read the docs for each component to learn more.

Learn more

The following talk from React Europe discusses how this project came to be, its architecture, high quality
cross-device interactions, and how we can share behavior between design systems.


Packages Dev Docs Pages Contribute (`packages/dev/docs/pages/contribute.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
import Contributing from '../../../../CONTRIBUTING.md';
export default Layout;


category: Foundation title: Contribute keywords: [react spectrum contributions, bug reports, feature requests]


Packages Dev Docs Pages Error (`packages/dev/docs/pages/error.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import {Content} from '@react-spectrum/view';
import {Flex} from '@react-spectrum/layout';
import {Heading} from '@react-spectrum/text';
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
import NotFound from '@spectrum-icons/illustrations/NotFound';


title: Page not found

text
<NotFound />
<Heading>Error 404: Page not found</Heading>
<Content>This page isn't available. Try checking the URL or visit a different page.</Content>

Packages Dev Docs Src Template (`packages/dev/docs/src/template.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/COMPONENT_NAME';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/COMPONENT_NAME/package.json';

jsx import
import {Component} from '@react-spectrum/COMPONENT_NAME';

category: Categoryname keywords: []

Component Name

{docs.exports.Component.description}

<HeaderInfo
packageData={packageData}
componentNames={['Component']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/web/rsp/components/button'}
]} />

Example

tsx example
<Button variant="accent">Button</Button>

Content

If the component has a children prop that accepts any type of content (e.g. ReactNode), include this section. Please include a note about how to internationalize the content.

Value

If the component displays or allows a user to input a value, include this section.

Labeling

If the component supports a label prop, include this section. Please include a note about how to internationalize the content.

Events

If the component supports event props, include this section. Only cover the events that are important to the main functionality of the component.

Validation

If the component supports validation props, include this section.

Props

Visual options

Show examples of all variants and visual props here with links to the design website for more usage details. Examples can be grouped together for conciseness.

Sample Option

View guidelines


Packages @Adobe React Spectrum Docs Accordion Accordion (`packages/@adobe/react-spectrum/docs/accordion/Accordion.mdx`)

{/* Copyright 2024 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/accordion';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/accordion/package.json';


category: Navigation keywords: [disclosure, accordion, collapse, expand]

jsx import
import {Accordion, Disclosure, DisclosureTitle, DisclosurePanel} from '@react-spectrum/accordion';

Accordion

{docs.exports.Accordion.description}

<HeaderInfo
packageData={packageData}
componentNames={['Accordion', 'Disclosure', 'DisclosureTitle', 'DisclosurePanel']}
since="3.38.0" />

Example

tsx example
<Accordion defaultExpandedKeys={['personal']}>
  <Disclosure id="personal">
    <DisclosureTitle>Personal Information</DisclosureTitle>
    <DisclosurePanel>
      Personal information form here.
    </DisclosurePanel>
  </Disclosure>
  <Disclosure id="billing">
    <DisclosureTitle>Billing Address</DisclosureTitle>
    <DisclosurePanel>
      Billing address form here.
    </DisclosurePanel>
  </Disclosure>
</Accordion>

Content

Accordion accepts multiple Disclosure elements as children. Each disclosure accepts an id prop which is passed to the onExpandedChange handler to identify the expanded disclosure within the accordion. See the Events section for more details about expansion.

Internationalization

In order to internationalize an Accordion, all text content within the accordion should be localized.

Events

Accordion accepts an onExpandedChange prop which is triggered when a disclosure is expanded or collapsed. The example below uses onExpandedChange to programmatically control disclosure expansion.

tsx example
import {Key} from "@react-types/shared";

function ControlledExpansion() {
  let [expandedKeys, setExpandedKeys] = React.useState<Set<Key>>(new Set(['personal']))

  return (
    <>
      <Accordion expandedKeys={expandedKeys} onExpandedChange={setExpandedKeys}>
        <Disclosure id="personal">
          <DisclosureTitle>Personal Information</DisclosureTitle>
          <DisclosurePanel>
            Personal information form here.
          </DisclosurePanel>
        </Disclosure>
        <Disclosure id="billing">
          <DisclosureTitle>Billing Address</DisclosureTitle>
          <DisclosurePanel>
            Billing address form here.
          </DisclosurePanel>
        </Disclosure>
      </Accordion>
      <div style={{marginTop: '20px'}}>You have expanded: {expandedKeys}</div>
    </>
  )
}

Expanded

By default, only one disclosure will be expanded at a time. To expand multiple disclosures, apply the allowsMultipleExpanded prop to Accordion.

tsx example
<Accordion allowsMultipleExpanded defaultExpandedKeys={['personal', 'billing']}>
  <Disclosure id="personal">
    <DisclosureTitle>Personal Information</DisclosureTitle>
    <DisclosurePanel>
      Personal information form here.
    </DisclosurePanel>
  </Disclosure>
  <Disclosure id="billing">
    <DisclosureTitle>Billing Address</DisclosureTitle>
    <DisclosurePanel>
      Billing address form here.
    </DisclosurePanel>
  </Disclosure>
</Accordion>

Props

Accordion

Disclosure

Disclosure Title

Disclosure Panel

Visual Options

Disabled

tsx example
<Accordion isDisabled>
  <Disclosure id="personal">
    <DisclosureTitle>Personal Information</DisclosureTitle>
    <DisclosurePanel>
      Personal information form here.
    </DisclosurePanel>
  </Disclosure>
  <Disclosure id="billing">
    <DisclosureTitle>Billing Address</DisclosureTitle>
    <DisclosurePanel>
      Billing address form here.
    </DisclosurePanel>
  </Disclosure>
</Accordion>

Quiet

tsx example
<Accordion isQuiet>
  <Disclosure id="personal">
    <DisclosureTitle>Personal Information</DisclosureTitle>
    <DisclosurePanel>
      Personal information form here.
    </DisclosurePanel>
  </Disclosure>
  <Disclosure id="billing">
    <DisclosureTitle>Billing Address</DisclosureTitle>
    <DisclosurePanel>
      Billing address form here.
    </DisclosurePanel>
  </Disclosure>
</Accordion>

Packages @Adobe React Spectrum Docs Accordion Disclosure (`packages/@adobe/react-spectrum/docs/accordion/Disclosure.mdx`)

{/* Copyright 2024 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/accordion';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/accordion/package.json';


category: Navigation keywords: [disclosure, accordion, collapse, expand]

jsx import
import {Disclosure, DisclosureTitle, DisclosurePanel} from '@react-spectrum/accordion';

Disclosure

{docs.exports.Disclosure.description}

<HeaderInfo
packageData={packageData}
componentNames={['Disclosure', 'DisclosureTitle', 'DisclosurePanel']}
since="3.38.0" />

Example

tsx example
<Disclosure>
  <DisclosureTitle>System Requirements</DisclosureTitle>
  <DisclosurePanel>
    Details about system requirements here.
  </DisclosurePanel>
</Disclosure>

Content

Disclosure follows the ARIA Disclosure pattern and consists of two children: DisclosureTitle and DisclosurePanel. The DisclosureTitle controls the expansion of the DisclosurePanel and the contents inside.

Disclosure can be used as a standalone collapsible section of content. However, multiple discosures can be combined to form an Accordion.

Internationalization

In order to internationalize a disclosure, all text content within the disclosure should be localized.

Events

Disclosure accepts an onExpandedChange prop which is triggered when it is expanded or collapsed. The example below uses onExpandedChange to programmatically control disclosure expansion.

tsx example
function ControlledExpansion() {
  let [isExpanded, setIsExpanded] = React.useState<boolean>(false);

  return (
    <>
      <Disclosure isExpanded={isExpanded} onExpandedChange={setIsExpanded}>
        <DisclosureTitle>System Requirements</DisclosureTitle>
        <DisclosurePanel>
          Details about system requirements here.
        </DisclosurePanel>
      </Disclosure>
      <div style={{marginTop: '20px'}}>{isExpanded ? 'The disclosure is expanded' : 'The disclosure is collapsed'}</div>
    </>
  )
}

Props

Disclosure

DisclosureTitle

DisclosurePanel

Visual Options

Disabled

tsx example
<Disclosure isDisabled>
  <DisclosureTitle>System Requirements</DisclosureTitle>
  <DisclosurePanel>
    Details about system requirements here.
  </DisclosurePanel>
</Disclosure>

Expanded

tsx example
<Disclosure defaultExpanded>
  <DisclosureTitle>System Requirements</DisclosureTitle>
  <DisclosurePanel>
    Details about system requirements here.
  </DisclosurePanel>
</Disclosure>

Quiet

tsx example
<Disclosure isQuiet>
  <DisclosureTitle>System Requirements</DisclosureTitle>
  <DisclosurePanel>
    Details about system requirements here.
  </DisclosurePanel>
</Disclosure>

Packages @Adobe React Spectrum Docs Actionbar ActionBar (`packages/@adobe/react-spectrum/docs/actionbar/ActionBar.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import Anatomy from './ActionBarAnatomy.svg';
import docs from 'docs:@react-spectrum/actionbar';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/actionbar/package.json';

jsx import
import {ActionBar, ActionBarContainer, Item} from '@react-spectrum/actionbar';
import {ListView} from '@react-spectrum/list';
import Edit from '@spectrum-icons/workflow/Edit';
import Copy from '@spectrum-icons/workflow/Copy';
import Delete from '@spectrum-icons/workflow/Delete';
import {Flex} from '@react-spectrum/layout';
import {Text} from '@react-spectrum/text';
import {TableView, TableHeader, TableBody, Row, Column, Cell} from '@react-spectrum/table';

category: Collections keywords: [actionbar, selection, actions, collections, toolbar]

ActionBar

{docs.exports.ActionBar.description}

<HeaderInfo
packageData={packageData}
componentNames={['ActionBar', 'ActionBarContainer']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/action-bar/'}
]}
since="3.27.0" />

Example

tsx example
import type {Selection} from '@adobe/react-spectrum';

function Example() {
  let [selectedKeys, setSelectedKeys] = React.useState<Selection>(new Set(['photoshop']));

  return (
    <ActionBarContainer height={300} maxWidth="size-6000">
      <ListView aria-label="ListView with action bar" selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys}>
        <Item key="photoshop">Adobe Photoshop</Item>
        <Item key="illustrator">Adobe Illustrator</Item>
        <Item key="xd">Adobe XD</Item>
      </ListView>
      <ActionBar
        isEmphasized
        selectedItemCount={selectedKeys === 'all' ? 'all' : selectedKeys.size}
        onAction={(key) => alert(`Performing ${key} action...`)}
        onClearSelection={() => setSelectedKeys(new Set())}>
        <Item key="edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="copy">
          <Copy />
          <Text>Copy</Text>
        </Item>
        <Item key="delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionBar>
    </ActionBarContainer>
  );
}

<Example />

Content

An ActionBar is a companion component intended to facilitate bulk actions on selected items within a collection component. It consists of an ActionGroup, a clear button, and a count of selected items. It accepts a list of
elements as children, each with a key prop. Alternatively, a function that returns Item elements is also supported as seen below. See the collection components
docs for more details about this API. These Item elements are used to populate the buttons in the ActionBar, and follows the ActionGroup content guidelines for customization and labeling.

tsx example
import type {Selection} from '@adobe/react-spectrum';

function Example() {
  let barItems = [
    {key: 'edit', label: 'Edit'},
    {key: 'copy', label: 'Copy'},
    {key: 'delete', label: 'Delete'}
  ];

  let [selectedKeys, setSelectedKeys] = React.useState<Selection>(new Set(['photoshop']));

  return (
    <ActionBarContainer height={300} maxWidth="size-6000">
      <ListView aria-label="ListView with action bar" selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys}>
        <Item key="photoshop">Adobe Photoshop</Item>
        <Item key="illustrator">Adobe Illustrator</Item>
        <Item key="xd">Adobe XD</Item>
      </ListView>
      <ActionBar
        items={barItems}
        selectedItemCount={selectedKeys === 'all' ? 'all' : selectedKeys.size}
        onAction={(key) => alert(`Performing ${key} action...`)}
        onClearSelection={() => setSelectedKeys(new Set())}
        isEmphasized>
        {item => (
          <Item key={item.key}>
            {item.label}
          </Item>
        )}
      </ActionBar>
    </ActionBarContainer>
  );
}

<Example />

Accessibility

The contents of the ActionBar should follow the same accessibility guidelines as ActionGroup's items, see the ActionGroup docs for more details.

Internationalization

To internationalize an ActionBar, the content provided to all child items should be localized. A localized string for the selection count and clear button aria-label will be provided
automatically. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ActionBar is automatically flipped.

Events

Use the onClearSelection prop as a callback to handle when the clear button in the ActionBar is pressed. Additionally, the onAction prop can be used as a callback for when the user
presses any of the buttons in the ActionBar. The key from the pressed `` will be passed to the callback.

The following example clears row selection within the TableView when the clear button is pressed and alerts the user as to what
action is being taken on the selected rows when the buttons are pressed.

text
/* Detailed source-code truncated for AI context efficiency. */

Props

ActionBarContainer props

ActionBar props

Visual options

isEmphasized

isEmphasized should be applied to the ActionBar when it should call attention, such as when floating in a TableView. It should be omitted if the ActionBar should blend in
with the rest of the UI, directing a user’s focus to elsewhere in a view.

tsx example
import type {Selection} from '@adobe/react-spectrum';

function Example({isEmphasized}: {isEmphasized?: boolean}) {
  let [selectedKeys, setSelectedKeys] = React.useState<Selection>(new Set(['photoshop']));

  return (
    <ActionBarContainer height={300} width="size-5000">
      <ListView aria-label="ListView with action bar" selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys}>
        <Item key="photoshop">Adobe Photoshop</Item>
        <Item key="illustrator">Adobe Illustrator</Item>
        <Item key="xd">Adobe XD</Item>
      </ListView>
      <ActionBar
        /*- begin highlight -*/
        isEmphasized={isEmphasized}
        /*- end highlight -*/
        selectedItemCount={selectedKeys === 'all' ? 'all' : selectedKeys.size}
        onAction={(key) => alert(`Performing ${key} action...`)}
        onClearSelection={() => setSelectedKeys(new Set())}>
        <Item key="edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="copy">
          <Copy />
          <Text>Copy</Text>
        </Item>
        <Item key="delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionBar>
    </ActionBarContainer>
  );
}

<Flex direction="row" gap="size-200" wrap>
  <Example />
  <Example isEmphasized />
</Flex>

isDisabled

To disable individual items, a list of disabledKeys can be provided.

tsx example
import type {Selection} from '@adobe/react-spectrum';

function Example() {
  let [selectedKeys, setSelectedKeys] = React.useState<Selection>(new Set(['photoshop']));

  return (
    <ActionBarContainer height={300} width="size-5000">
      <ListView aria-label="ListView with action bar" selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys}>
        <Item key="photoshop">Adobe Photoshop</Item>
        <Item key="illustrator">Adobe Illustrator</Item>
        <Item key="xd">Adobe XD</Item>
      </ListView>
      <ActionBar
        /*- begin highlight -*/
        disabledKeys={['edit']}
        /*- end highlight -*/
        isEmphasized={true}
        selectedItemCount={selectedKeys === 'all' ? 'all' : selectedKeys.size}
        onAction={(key) => alert(`Performing ${key} action...`)}
        onClearSelection={() => setSelectedKeys(new Set())}>
        <Item key="edit">
          <Edit />
          <Text>Edit</Text>
        </Item>
        <Item key="copy">
          <Copy />
          <Text>Copy</Text>
        </Item>
        <Item key="delete">
          <Delete />
          <Text>Delete</Text>
        </Item>
      </ActionBar>
    </ActionBarContainer>
  );
}

<Example />

Packages @Adobe React Spectrum Docs Actiongroup ActionGroup (`packages/@adobe/react-spectrum/docs/actiongroup/ActionGroup.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/actiongroup';
import collectionsDocs from 'docs:@react-stately/collections';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/actiongroup/package.json';

jsx import
import {ActionGroup, Item} from '@react-spectrum/actiongroup';
import Brush from '@spectrum-icons/workflow/Brush';
import RegionSelect from '@spectrum-icons/workflow/RegionSelect';
import Select from '@spectrum-icons/workflow/Select';
import {Flex} from '@react-spectrum/layout';
import {View} from '@react-spectrum/view';
import ViewList from '@spectrum-icons/workflow/ViewList';
import ViewGrid from '@spectrum-icons/workflow/ViewGrid';
import ViewCard from '@spectrum-icons/workflow/ViewCard';
import Move from '@spectrum-icons/workflow/Move';
import Duplicate from '@spectrum-icons/workflow/Duplicate';
import TagBold from '@spectrum-icons/workflow/TagBold';
import TagItalic from '@spectrum-icons/workflow/TagItalic';
import TagUnderline from '@spectrum-icons/workflow/TagUnderline';
import TextStrikethrough from '@spectrum-icons/workflow/TextStrikethrough';
import TextAlignCenter from '@spectrum-icons/workflow/TextAlignCenter';
import TextAlignJustify from '@spectrum-icons/workflow/TextAlignJustify';
import TextAlignLeft from '@spectrum-icons/workflow/TextAlignLeft';
import TextAlignRight from '@spectrum-icons/workflow/TextAlignRight';

category: Buttons keywords: [toolbar, actiongroup, collections]

ActionGroup

{docs.exports.ActionGroup.description}

<HeaderInfo
packageData={packageData}
componentNames={['ActionGroup', 'Item']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/action-group/'}
]}
since="3.0.0" />

Example

tsx example
function Example() {
  let [action, setAction] = React.useState(null);

  return (
    <>
      <ActionGroup onAction={setAction}>
        <Item key="add">Add</Item>
        <Item key="delete">Delete</Item>
        <Item key="edit">Edit</Item>
      </ActionGroup>
      <p>Action: {action}</p>
    </>
  );
}

Content

ActionGroups accept a list of elements as children, each with a key prop. Alternatively, a function that returns Item elements is also supported. See the collection components docs for more details about this API.

tsx example
const items = [
  {label: 'React', name: 'React'},
  {label: 'Add', name: 'Add'},
  {label: 'Delete', name: 'Delete'}
];

<ActionGroup items={items}>
  {item => <Item key={item.name}>{item.label}</Item>}
</ActionGroup>

Icons

Icons can be added to ActionGroup items by wrapping the label in a Text element, and adding the icon as a sibling.

tsx example
import Draw from '@spectrum-icons/workflow/Draw';
import Copy from '@spectrum-icons/workflow/Copy';
import Delete from '@spectrum-icons/workflow/Delete';
import {Text} from '@react-spectrum/text';

<ActionGroup>
  <Item key="edit">
    <Draw />
    <Text>Edit</Text>
  </Item>
  <Item key="copy">
    <Copy />
    <Text>Copy</Text>
  </Item>
  <Item key="delete">
    <Delete />
    <Text>Delete</Text>
  </Item>
</ActionGroup>

You can also hide the label text within the buttons using the buttonLabelBehavior prop. When set to "hide", the label is
hidden and automatically shown in a tooltip on hover.

tsx example
<ActionGroup buttonLabelBehavior="hide">
  <Item key="edit">
    <Draw />
    <Text>Edit</Text>
  </Item>
  <Item key="copy">
    <Copy />
    <Text>Copy</Text>
  </Item>
  <Item key="delete">
    <Delete />
    <Text>Delete</Text>
  </Item>
</ActionGroup>

Accessibility

Icon only ActionGroups should usually include a tooltip to describe the button via the buttonLabelBehavior prop as described above. This also automatically handles
labeling the button for assistive technology users.

In rare cases where no tooltip is desired, an alternative text label must be provided to identify the control for accessibility.
This should be added using the aria-label prop to each Item.

tsx example
<ActionGroup>
  <Item key="brush" aria-label="Brush"><Brush /></Item>
  <Item key="select" aria-label="Select"><Select /></Item>
  <Item key="regionSelect" aria-label="Select Region"><RegionSelect /></Item>
</ActionGroup>

Internationalization

In order to internationalize an ActionGroup, the content provided to all child items should be localized.

Selection

ActionGroup supports multiple selection modes. By default, selection is disabled, however this can be changed using the selectionMode prop.
Use defaultSelectedKeys to provide a default set of selected items (uncontrolled) and selectedKeys to set the selected items (controlled). The value of the selected keys must match the key prop of the items.
See the react-stately Selection docs for caveats regarding selection prop typing.

tsx example
<ActionGroup selectionMode="single" defaultSelectedKeys={['list']}>
  <Item key="grid">Grid view</Item>
  <Item key="list">List view</Item>
  <Item key="gallery">Gallery view</Item>
</ActionGroup>

The selectedKeys prop can be used to make the selected state controlled.

tsx example
import type {Selection} from '@adobe/react-spectrum';

function Example() {
  let [selected, setSelected] = React.useState<Selection>(new Set(['list']));

  return (
    <>
      <ActionGroup selectionMode="single" selectedKeys={selected} onSelectionChange={setSelected}>
        <Item key="grid">Grid view</Item>
        <Item key="list">List view</Item>
        <Item key="gallery">Gallery view</Item>
      </ActionGroup>
      <p>Current selection (controlled): {[...selected]}</p>
    </>
  );
}

Set selectionMode prop to multiple to allow more than one selection.

tsx example
function Example() {
  let [selected, setSelected] = React.useState<Selection>(new Set(['list']));

  return (
    <>
      <ActionGroup selectionMode="multiple" selectedKeys={selected} onSelectionChange={setSelected}>
        <Item key="grid">Grid view</Item>
        <Item key="list">List view</Item>
        <Item key="gallery">Gallery view</Item>
      </ActionGroup>
      <p>Current selections (controlled): {[...selected].join(', ')}</p>
    </>
  );
}

Events

Use the onSelectionChange prop as a callback to handle press events on items when selectionMode is either single or multiple.
The key prop from the selected item will be passed into the callback.

To continue to capturing actions when selectionMode is none, utilize the onAction prop.

tsx example
import type {Key} from '@adobe/react-spectrum';

function Example() {
  let [actionKey, setActionKey] = React.useState<Key>('');
  return (
    <>
      <ActionGroup onAction={setActionKey}>
        <Item key="add">Add</Item>
        <Item key="delete">Delete</Item>
        <Item key="edit">Edit</Item>
      </ActionGroup>
      <p>Action: {actionKey}</p>
    </>
  );
}

Collapsing behavior

By default, ActionGroup items wrap to form a new line when horizontal space is limited. However, this can cause content to shift below the
ActionGroup. If the ActionGroup should always appear in a single line, the overflowMode prop can be set to collapse. In this mode, when
horizontal space is limited, the ActionGroup will collapse into a menu. The exact behavior depends on the selectionMode.

Non-selectable

When selection is not enabled, ActionGroup displays as many items as possible and collapses the remaining items into a more actions menu.

tsx example
<ActionGroup overflowMode="collapse" maxWidth={250}>
  <Item key="edit">
    <Draw />
    <Text>Edit</Text>
  </Item>
  <Item key="copy">
    <Copy />
    <Text>Copy</Text>
  </Item>
  <Item key="delete">
    <Delete />
    <Text>Delete</Text>
  </Item>
  <Item key="move">
    <Move />
    <Text>Move</Text>
  </Item>
  <Item key="duplicate">
    <Duplicate />
    <Text>Duplicate</Text>
  </Item>
</ActionGroup>

Selection

When selection is enabled, ActionGroup collapses all items into a menu together when space is limited. The menu button indicates when one of
the options within it is selected by showing a highlighted state. A summaryIcon should be specified to visually communicate the purpose of
the ActionGroup when collapsed, and an aria-label should be provided to describe the ActionGroup to assistive technology.

This example shows a multi-selectable ActionGroup that can be used to select text styles. When space is limited, it collapses into a dropdown
menu, and displays the TextStyle icon passed to the summaryIcon prop.

tsx example
import TextStyle from '@spectrum-icons/workflow/TextStyle';

<ActionGroup
  aria-label="Text style"
  overflowMode="collapse"
  selectionMode="multiple"
  isEmphasized
  summaryIcon={<TextStyle />}
  maxWidth={100}>
  <Item key="bold">
    <TagBold />
    <Text>Bold</Text>
  </Item>
  <Item key="italic">
    <TagItalic />
    <Text>Italic</Text>
  </Item>
  <Item key="underline">
    <TagUnderline />
    <Text>Underline</Text>
  </Item>
  <Item key="strike">
    <TextStrikethrough />
    <Text>Strikethrough</Text>
  </Item>
</ActionGroup>

Single selection

A special case where a summaryIcon is not needed is a single selectable ActionGroup (selectionMode="single") which enforces that an item
is always selected (disallowEmptySelection). In this case, the selected item is displayed inside the menu button when collapsed.

tsx example
<ActionGroup
  aria-label="Text alignment"
  overflowMode="collapse"
  selectionMode="single"
  defaultSelectedKeys={['left']}
  disallowEmptySelection
  buttonLabelBehavior="hide"
  isEmphasized
  maxWidth={100}>
  <Item key="left">
    <TextAlignLeft />
    <Text>Align Left</Text>
  </Item>
  <Item key="center">
    <TextAlignCenter />
    <Text>Align Center</Text>
  </Item>
  <Item key="right">
    <TextAlignRight />
    <Text>Align Right</Text>
  </Item>
  <Item key="justify">
    <TextAlignJustify />
    <Text>Justify</Text>
  </Item>
</ActionGroup>

Collapsing button labels

In addition to collapsing items that don't fit into a menu, the button labels can also be automatically hidden when space is limited.
This keeps more buttons visible at a time before needing to collapse into a menu. This behavior can be enabled by setting the
buttonLabelBehavior prop to "collapse". The labels will be automatically shown in tooltips when collapsed.

tsx example
<ActionGroup overflowMode="collapse" buttonLabelBehavior="collapse" maxWidth={180}>
  <Item key="edit">
    <Draw />
    <Text>Edit</Text>
  </Item>
  <Item key="copy">
    <Copy />
    <Text>Copy</Text>
  </Item>
  <Item key="delete">
    <Delete />
    <Text>Delete</Text>
  </Item>
  <Item key="move">
    <Move />
    <Text>Move</Text>
  </Item>
  <Item key="duplicate">
    <Duplicate />
    <Text>Duplicate</Text>
  </Item>
</ActionGroup>

Vertical collapsing behavior

Non-selectable vertical ActionGroups also support collapsing when the height is too small to fit all of the buttons.
The behavior is the same as for horizontal ActionGroups, except for the positioning of the tooltips and menu.

Vertical ActionGroups with selection do not currently support collapsing. When selection is enabled, we recommend placing
the ActionGroup in a scrollable container so that the user can access all items.

tsx example
<ActionGroup overflowMode="collapse" orientation="vertical" buttonLabelBehavior="hide" maxHeight={150}>
  <Item key="edit">
    <Draw />
    <Text>Edit</Text>
  </Item>
  <Item key="copy">
    <Copy />
    <Text>Copy</Text>
  </Item>
  <Item key="delete">
    <Delete />
    <Text>Delete</Text>
  </Item>
  <Item key="move">
    <Move />
    <Text>Move</Text>
  </Item>
  <Item key="duplicate">
    <Duplicate />
    <Text>Duplicate</Text>
  </Item>
</ActionGroup>

Props

Visual options

Quiet

tsx example
<ActionGroup isQuiet>
  <Item key="add">Add</Item>
  <Item key="delete">Delete</Item>
  <Item key="edit">Edit</Item>
</ActionGroup>

Emphasized

The additional styling provided by the isEmphasized prop is only applied when items are selected.

tsx example
<ActionGroup
  isEmphasized
  selectionMode="single"
  defaultSelectedKeys={['list']}>
  <Item key="grid">Grid view</Item>
  <Item key="list">List view</Item>
  <Item key="gallery">Gallery view</Item>
</ActionGroup>

Static color

The staticColor prop can be used when the ActionGroup is displayed over a color background. When selected, the icon and text in each
button automatically take on the color of the background. You are responsible for choosing the static color variant that ensures the
text meets an accessible contrast ratio
with the background.

tsx example
<Flex wrap gap="size-250">
  <View backgroundColor="static-blue-700" padding="size-500">
    <ActionGroup staticColor="white">
      <Item key="edit">
        <Draw />
        <Text>Edit</Text>
      </Item>
      <Item key="copy">
        <Copy />
        <Text>Copy</Text>
      </Item>
      <Item key="delete">
        <Delete />
        <Text>Delete</Text>
      </Item>
    </ActionGroup>
  </View>
  <View backgroundColor="static-yellow-400" padding="size-500">
    <ActionGroup
      staticColor="black"
      isQuiet
      buttonLabelBehavior="hide"
      selectionMode="single"
      disallowEmptySelection
      defaultSelectedKeys={['list']}>
      <Item key="list">
        <ViewList />
        <Text>List view</Text>
      </Item>
      <Item key="grid">
        <ViewGrid />
        <Text>Grid view</Text>
      </Item>
      <Item key="gallery">
        <ViewCard />
        <Text>Gallery view</Text>
      </Item>
    </ActionGroup>
  </View>
</Flex>

Disabled

To disable the entire ActionGroup, use the isDisabled prop on the root.

tsx example
<ActionGroup isDisabled>
  <Item key="add">Add</Item>
  <Item key="delete">Delete</Item>
  <Item key="edit">Edit</Item>
</ActionGroup>

To disable individual items, a list of disabledKeys can be provided.

tsx example
<ActionGroup disabledKeys={['add', 'delete']}>
  <Item key="add">Add</Item>
  <Item key="delete">Delete</Item>
  <Item key="edit">Edit</Item>
</ActionGroup>

Orientation

Using the orientation prop with value vertical changes the alignment of the items to follow the y-axis.

tsx example
<ActionGroup orientation="vertical">
  <Item key="brush" aria-label="Brush"><Brush /></Item>
  <Item key="select" aria-label="Select"><Select /></Item>
  <Item key="regionSelect" aria-label="Select Region"><RegionSelect /></Item>
</ActionGroup>

Density

Using the density prop with value compact changes the margins between the buttons.
In the default case, this merges the borders of neighboring ActionButtons. In the
quiet case, it just reduces the margin size between the buttons.

tsx example
<ActionGroup density="compact">
  <Item key="brush" aria-label="Brush"><Brush /></Item>
  <Item key="select" aria-label="Select"><Select /></Item>
  <Item key="regionSelect" aria-label="Select Region"><RegionSelect /></Item>
</ActionGroup>
tsx example
<ActionGroup isQuiet density="compact">
  <Item key="brush" aria-label="Brush"><Brush /></Item>
  <Item key="select" aria-label="Select"><Select /></Item>
  <Item key="regionSelect" aria-label="Select Region"><RegionSelect /></Item>
</ActionGroup>

Justified

The isJustified prop will divide all available horizontal space evenly among the buttons.

tsx example
<Flex width="size-2000" direction="column">
  <ActionGroup isJustified>
    <Item key="brush" aria-label="Brush"><Brush /></Item>
    <Item key="select" aria-label="Select"><Select /></Item>
    <Item key="regionSelect" aria-label="Select Region"><RegionSelect /></Item>
  </ActionGroup>
</Flex>

Packages @Adobe React Spectrum Docs Autocomplete SearchAutocomplete (`packages/@adobe/react-spectrum/docs/autocomplete/SearchAutocomplete.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/autocomplete';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/autocomplete/package.json';

jsx import
import {SearchAutocomplete, Section, Item} from '@react-spectrum/autocomplete';
import {useFilter} from '@react-aria/i18n';
import Email from '@spectrum-icons/workflow/Email';
import Document from '@spectrum-icons/workflow/Document';
import WebPages from '@spectrum-icons/workflow/WebPages';
import SocialNetwork from '@spectrum-icons/workflow/SocialNetwork';
import ShoppingCart from '@spectrum-icons/workflow/ShoppingCart';
import Folder from '@spectrum-icons/workflow/Folder';
import {Text} from '@react-spectrum/text';
import {Avatar} from "@react-spectrum/avatar";

category: Pickers keywords: [search field, input] after_version: 3.0.0

SearchAutocomplete

{docs.exports.SearchAutocomplete.description}

<HeaderInfo
packageData={packageData}
componentNames={['SearchAutocomplete', 'Section', 'Item']} />

Example

tsx example
<SearchAutocomplete label="Search with Autocomplete">
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Content

SearchAutocomplete follows the Collection Components API, accepting both static and dynamic collections.
Similar to ComboBox, SearchAutocomplete accepts `` elements as children, each with a key prop. Basic usage of SearchAutocomplete, seen in the example above, shows multiple options populated with a string.
Static collections, as in this example, can be used when the full list of options is known ahead of time.

Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time.
Providing the data in this way allows SearchAutocomplete to automatically cache the rendering of each item, which dramatically improves performance.

As seen below, an iterable list of options is passed to the SearchAutocomplete using the defaultItems prop.

tsx example
function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];

  return (
    <SearchAutocomplete
      label="Search engineering majors"
      defaultItems={options}>
      {item => <Item>{item.name}</Item>}
    </SearchAutocomplete>
  );
}

Alternatively, passing your list of options to SearchAutocomplete's items prop will cause the list of items to be controlled, useful for when you want to provide your own
filtering logic. See the Custom Filtering section for more detail.

Internationalization

To internationalize a SearchAutocomplete, a localized string should be passed to the children of each Item.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the SearchAutocomplete is automatically flipped.

Labeling

SearchAutocomplete can be labeled using the label prop. If the SearchAutocomplete is a required field, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<SearchAutocomplete label="Favorite Animal" isRequired necessityIndicator="icon">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>
tsx example
<SearchAutocomplete label="Favorite Animal" isRequired necessityIndicator="label">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>
tsx example
<SearchAutocomplete label="Favorite Animal" necessityIndicator="label">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the SearchAutocomplete for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a SearchAutocomplete, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

By default, interacting with an item in a SearchAutocomplete updates the input value. Alternatively, items may be links to another page or website. This can be achieved by passing the href prop to the `` component. Interacting with link items navigates to the provided URL and does not update the input value.

tsx example
<SearchAutocomplete label="Tech company websites">
  <Item href="https://adobe.com/" target="_blank">Adobe</Item>
  <Item href="https://apple.com/" target="_blank">Apple</Item>
  <Item href="https://google.com/" target="_blank">Google</Item>
  <Item href="https://microsoft.com/" target="_blank">Microsoft</Item>
</SearchAutocomplete>

Client side routing

The `` component works with frameworks and client side routers like Next.js and React Router. As with other React Spectrum components that support links, this works via the Provider component at the root of your app. See the client side routing guide to learn how to set this up.

Sections

SearchAutocomplete supports sections in order to group options. Sections can be used by wrapping groups of items in a Section element. Each Section takes a title and key prop.

Static items

tsx example
<SearchAutocomplete label="Preferred fruit or vegetable">
  <Section title="Fruit">
    <Item key="Apple">Apple</Item>
    <Item key="Banana">Banana</Item>
    <Item key="Orange">Orange</Item>
    <Item key="Honeydew">Honeydew</Item>
    <Item key="Grapes">Grapes</Item>
    <Item key="Watermelon">Watermelon</Item>
    <Item key="Cantaloupe">Cantaloupe</Item>
    <Item key="Pear">Pear</Item>
  </Section>
  <Section title="Vegetable">
    <Item key="Cabbage">Cabbage</Item>
    <Item key="Broccoli">Broccoli</Item>
    <Item key="Carrots">Carrots</Item>
    <Item key="Lettuce">Lettuce</Item>
    <Item key="Spinach">Spinach</Item>
    <Item key="Bok Choy">Bok Choy</Item>
    <Item key="Cauliflower">Cauliflower</Item>
    <Item key="Potatoes">Potatoes</Item>
  </Section>
</SearchAutocomplete>

Dynamic items

Sections used with dynamic items are populated from a hierarchical data structure. Please note that Section takes an array of data using the items prop only.

tsx example
function Example() {
  let options = [
    {name: 'Fruit', children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Orange'},
      {name: 'Honeydew'},
      {name: 'Grapes'},
      {name: 'Watermelon'},
      {name: 'Cantaloupe'},
      {name: 'Pear'}
    ]},
    {name: 'Vegetable', children: [
      {name: 'Cabbage'},
      {name: 'Broccoli'},
      {name: 'Carrots'},
      {name: 'Lettuce'},
      {name: 'Spinach'},
      {name: 'Bok Choy'},
      {name: 'Cauliflower'},
      {name: 'Potatoes'}
    ]}
  ];

  return (
    <SearchAutocomplete label="Preferred fruit or vegetable" defaultItems={options}>
      {item => (
        <Section key={item.name} items={item.children} title={item.name}>
          {item => <Item key={item.name}>{item.name}</Item>}
        </Section>
      )}
    </SearchAutocomplete>
  );
}

Complex items

Items within SearchAutocomplete also allow for additional content used to better communicate options. Icons, avatars, and descriptions can be added to the children of Item as shown in the example below.
If a description is added, the prop slot="description" must be used to distinguish the different <Text> elements.
See Icon's labeling section and Avatar's accessibility section for more information on how to label these elements.

tsx example
<SearchAutocomplete label="Search apps">
  <Section title="Productivity">
    <Item textValue="Mail">
      <Email size="S" />
      <Text>Mail</Text>
      <Text slot="description">Send and receive emails</Text>
    </Item>
    <Item textValue="File Explorer">
      <Folder size="S" />
      <Text>File Explorer</Text>
      <Text slot="description">Navigate directories and open files</Text>
    </Item>
    <Item textValue="Document Editor">
      <Document size="S" />
      <Text>Document Editor</Text>
      <Text slot="description">Edit documents</Text>
    </Item>
  </Section>
  <Section title="Internet">
    <Item textValue="Web Browser">
      <WebPages size="S" />
      <Text>Web Browser</Text>
      <Text slot="description">Browse the internet</Text>
    </Item>
    <Item textValue="Social Media">
      <SocialNetwork size="S" />
      <Text>Social Media</Text>
      <Text slot="description">Connect with friends</Text>
    </Item>
    <Item textValue="Shopping">
      <ShoppingCart size="S" />
      <Text>Shopping</Text>
      <Text slot="description">Shop online</Text>
    </Item>
  </Section>
</SearchAutocomplete>

With avatars

tsx example
<SearchAutocomplete label="Search users">
  <Item textValue="User 1">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 1</Text>
  </Item>
  <Item textValue="User 2">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 2</Text>
  </Item>
  <Item textValue="User 3">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 3</Text>
  </Item>
  <Item textValue="User 4">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 4</Text>
  </Item>
</SearchAutocomplete>

Asynchronous loading

SearchAutocomplete supports loading data asynchronously, and will display a progress circle reflecting the current load state,
set by the loadingState prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the onLoadMore prop.

This example uses the useAsyncList hook to handle loading the data. See the docs for more information.

tsx example
import {useAsyncList} from '@react-stately/data';

interface Character {
  name: string
}

function AsyncLoadingExample() {
  let list = useAsyncList<Character>({
    async load({signal, cursor, filterText}) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      // If no cursor is available, then we're loading the first page,
      // filtering the results returned via a query string that
      // mirrors the SearchAutocomplete input text.
      // Otherwise, the cursor is the next URL to load,
      // as returned from the previous page.
      let res = await fetch(cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`, {signal});
      let json = await res.json();

      return {
        items: json.results,
        cursor: json.next
      };
    }
  });

  return (
    <SearchAutocomplete
      label="Star Wars Character Lookup"
      items={list.items}
      inputValue={list.filterText}
      onInputChange={list.setFilterText}
      loadingState={list.loadingState}
      onLoadMore={list.loadMore}>
      {item => <Item key={item.name}>{item.name}</Item>}
    </SearchAutocomplete>
  );
}

Validation

SearchAutocomplete supports the isRequired prop to ensure the user enters a value, as well as custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the search field or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <SearchAutocomplete label="Favorite animal" name="animal" isRequired>
  {/*- end highlight -*/}
    <Item>Aardvark</Item>
    <Item>Cat</Item>
    <Item>Dog</Item>
    <Item>Kangaroo</Item>
    <Item>Panda</Item>
    <Item>Snake</Item>
  </SearchAutocomplete>
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, SearchAutocomplete displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Custom Filtering

By default, SearchAutocomplete uses a string "contains" filtering strategy when deciding what items to display in the dropdown menu. This filtering strategy can be overwritten
by filtering the list of items yourself and passing the filtered list to the SearchAutocomplete via the items prop.

The example below uses a string "startsWith" filter function obtained from the useFilter hook to display items that start with the SearchAutocomplete's current input
value only. By using the menuTrigger returned by onOpenChange, it also handles displaying the entire option list regardless of the current filter value when the SearchAutocomplete menu is
opened via the trigger button or arrow keys. menuTrigger tells you if the menu was opened manually by the user ("manual"), by focusing the SearchAutocomplete ("focus"), or by
changes in the input field ("input"), allowing you to make updates to other controlled aspects of your SearchAutocomplete accordingly.

tsx example
function Example() {
  let options = [
    {id: 1, email: '[email protected]'},
    {id: 2, email: '[email protected]'},
    {id: 3, email: '[email protected]'},
    {id: 4, email: '[email protected]'},
    {id: 5, email: '[email protected]'},
    {id: 6, email: '[email protected]'},
    {id: 7, email: '[email protected]'},
    {id: 8, email: '[email protected]'},
    {id: 9, email: '[email protected]'}
  ];

  let [showAll, setShowAll] = React.useState(false);
  let [filterValue, setFilterValue] = React.useState('');
  let {startsWith} = useFilter({sensitivity: 'base'});
  let filteredItems = React.useMemo(() => options.filter(item => startsWith(item.email, filterValue)), [options, filterValue]);

  return (
    <SearchAutocomplete
      onOpenChange={(isOpen, menuTrigger) => {
        // Show all items if menu is opened manually
        // i.e. by the arrow keys or trigger button
        if (menuTrigger === 'manual' && isOpen) {
          setShowAll(true);
        }
      }}
      width="size-3000"
      label="Search Email Addresses"
      items={showAll ? options : filteredItems}
      inputValue={filterValue}
      onInputChange={(value) => {
        setShowAll(false);
        setFilterValue(value);
      }}>
      {item => <Item>{item.email}</Item>}
    </SearchAutocomplete>
  );
}

Trigger options

By default, the SearchAutocomplete's menu is opened when the user types into the input field ("input"). There are two other supported modes: one where the menu opens when the SearchAutocomplete is focused ("focus") and
the other where the menu only opens when the user clicks or taps on the SearchAutocomplete's field button ("manual"). These can be set by providing "focus" or "manual" to the menuTrigger prop.
Guidelines on when to use a specific mode can be found here. Note that the mobile SearchAutocomplete experience requires the end user to press the SearchAutocomplete button
to open the tray regardless of the menuTrigger setting.

tsx example
<SearchAutocomplete label="Favorite Animal" menuTrigger="focus">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>
tsx example
<SearchAutocomplete label="Favorite Animal" menuTrigger="manual">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Props

Visual options

Label alignment and position

By default, the label is positioned above the SearchAutocomplete. The labelPosition prop can be used to position the label to the side.
The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the SearchAutocomplete and "end" refers to the right most edge.
For right-to-left (RTL) languages, this is flipped.

tsx example
<SearchAutocomplete label="Favorite Animal" labelPosition="side" labelAlign="end">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Quiet

tsx example
<SearchAutocomplete label="Favorite Animal" isQuiet>
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Disabled

tsx example
<SearchAutocomplete label="Favorite Animal" isDisabled>
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Read only

tsx example
<SearchAutocomplete label="Search Animals" isReadOnly>
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Custom widths

tsx example
<SearchAutocomplete label="Favorite Animal" width="size-6000" maxWidth="100%">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>
tsx example
<SearchAutocomplete label="Favorite Animal" direction="top">
  <Item>Red Panda</Item>
  <Item>Cat</Item>
  <Item>Dog</Item>
  <Item>Aardvark</Item>
  <Item>Kangaroo</Item>
  <Item>Snake</Item>
</SearchAutocomplete>

Testing

The SearchAutocomplete features an overlay that transitions in and out of the page as it is opened and closed. Depending on
your device configuration, this overlay may render as a tray or a dropdown and the trigger itself may render as a button or a input. Additionally,
the SearchAutocomplete features automatic virtualization and may need specific mocks in a test environment to enable said virtualization properly.
Please see the following sections in the testing docs for more information on how to handle these
behaviors in your test suite.

Timers

Desktop vs Mobile

Virtualized Components

Long press

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Avatar Avatar (`packages/@adobe/react-spectrum/docs/avatar/Avatar.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/avatar';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/avatar/package.json';

jsx import
import {Avatar} from '@react-spectrum/avatar';
import {Flex} from '@react-spectrum/layout';

category: Content keywords: [avatar]

Avatar

{docs.exports.Avatar.description}

<HeaderInfo
packageData={packageData}
componentNames={['Avatar']}
sourceData={[{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/avatar/'}]}
since="3.26.0" />

Example

tsx example
<Avatar src="https://i.imgur.com/kJOwAdv.png" alt="default Adobe avatar" />

Content

The Avatar component is a thumbnail representation of an entity, such as a user or an organization. They accept a src attribute defining the image URL, and an alt attribute for an accessible description.

Accessibility

By default, avatars are decorative and have an empty alt attribute. Standalone avatars with no surrounding context must have a custom alt prop defined for accessibility.

Internationalization

To internationalize an Avatar, a localized string should be passed to the alt prop.

Props

Visual options

Disabled

View guidelines

tsx example
<Avatar src="https://i.imgur.com/kJOwAdv.png" alt="default Adobe avatar" isDisabled />

Size

View guidelines

Sizes can either be one of the predefined avatar size variables, or a custom size in pixels.

tsx example
<Flex gap="size-100" wrap>
  {[50, 75, 100, 200, 300, 400, 500, 600, 700].map(size => (
    <Avatar key={size} src="https://i.imgur.com/kJOwAdv.png" alt="default Adobe avatar" size={`avatar-size-${size}`} />
  ))}
  <Avatar src="https://i.imgur.com/kJOwAdv.png" alt="avatar with custom size" size={50} />
</Flex>

Packages @Adobe React Spectrum Docs Badge Badge (`packages/@adobe/react-spectrum/docs/badge/Badge.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/badge';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/badge/package.json';

jsx import
import {Badge} from '@react-spectrum/badge';

category: Status keywords: [badge]

Badge

{docs.exports.Badge.description}

<HeaderInfo
packageData={packageData}
componentNames={['Badge']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/badge/'}
]}
since="3.22.0" />

Example

tsx example
<Badge variant="positive">Licensed</Badge>

Content

Badges can have a label, an icon, or both. An icon is provided by passing an icon component as a child to the Badge.
A visible label can be provided by passing a string or a Text component as a child, depending on whether the Badge has an accompanying icon.

tsx example
import {Text} from '@react-spectrum/text';
import CheckmarkCircle from '@spectrum-icons/workflow/CheckmarkCircle';

<Badge variant="positive">
  <CheckmarkCircle aria-label="Done" />
  <Text>Icon + Label</Text>
</Badge>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the icon for accessibility.

Internationalization

To internationalize a Badge, a localized string should be set as the child content of the Badge.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the Badge is automatically flipped.

Props

Visual options

Variant

View guidelines

When badges have a semantic meaning, they should use semantic colors.
Use the appropriate color to indicate status as follows.

tsx example
import {Flex} from '@react-spectrum/layout';

<Flex direction="column" gap={8}>
  <Badge variant="positive">Green: Approved, Complete, Success, New, Purchased, Licensed</Badge>
  <Badge variant="info">Blue: Active, In Use, Live, Published</Badge>
  <Badge variant="negative">Red: Error, Alert, Rejected, Failed</Badge>
  <Badge variant="neutral">Gray: Archived, Deleted, Paused, Draft, Not Started, Ended</Badge>
</Flex>

When badges are used to color code categories, they use label colors. The ideal usage for these is when there
are 8 or fewer categories being color coded.

tsx example
import {Flex} from '@react-spectrum/layout';

<Flex direction="column" gap={8}>
  <Badge variant="seafoam">Seafoam</Badge>
  <Badge variant="indigo">Indigo</Badge>
  <Badge variant="purple">Purple</Badge>
  <Badge variant="fuchsia">Fuchsia</Badge>
  <Badge variant="magenta">Magenta</Badge>
  <Badge variant="yellow">Yellow</Badge>
</Flex>

Packages @Adobe React Spectrum Docs Breadcrumbs Breadcrumbs (`packages/@adobe/react-spectrum/docs/breadcrumbs/Breadcrumbs.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/breadcrumbs';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/breadcrumbs/package.json';

jsx import
import {Breadcrumbs, Item} from '@react-spectrum/breadcrumbs';
import {View} from '@react-spectrum/view';

category: Navigation keywords: [collections, dropdown]

{docs.exports.Breadcrumbs.description}

<HeaderInfo
packageData={packageData}
componentNames={['Breadcrumbs', 'Item']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/breadcrumbs/'}
]}
since="3.0.0" />

Example

tsx example
<Breadcrumbs>
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
  <Item key="march 2020 assets">March 2020 Assets</Item>
</Breadcrumbs>

Content

Breadcrumbs follow the Collection Components API, accepting only static children.
Similar to Menu, Breadcrumbs accepts <> elements as children,
each with a key prop, which is passed to the onAction handler to identify the selected item.
Basic usage of Breadcrumbs, as seen in the example above, shows a list of elements,
populated with a string. See Events for more information.

Internationalization

In order to internationalize Breadcrumbs, the content of all child items should be localized.

Events

Use the onAction prop as a callback to handle press events on items.

tsx example
function Example() {
  let folders = [
    {id: 1, label: 'Home'},
    {id: 2, label: 'Trendy'},
    {id: 3, label: 'March 2020 Assets'}
  ];
  let [folderId, setFolderId] = React.useState(null);
  return (
    <div>
      <Breadcrumbs onAction={(a) => setFolderId(a)}>
        {folders.map(f => <Item key={f.id}>{f.label}</Item>)}
      </Breadcrumbs>
      <p>You pressed folder ID: {folderId}</p>
    </div>
  );
}

By default, interacting with an item in Breadcrumbs triggers onAction. Items may also be links to another page or website. This can be achieved by passing the href prop to the `` component.

tsx example
<Breadcrumbs>
  <Item href="#">Home</Item>
  <Item href="#">React Spectrum</Item>
  <Item>Breadcrumbs</Item>
</Breadcrumbs>

Client side routing

The `` component works with frameworks and client side routers like Next.js and React Router. As with other React Spectrum components that support links, this works via the Provider component at the root of your app. See the client side routing guide to learn how to set this up.

Props

Visual options

Size

View guidelines

Small

tsx example
<Breadcrumbs size="S">
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
</Breadcrumbs>

Medium

tsx example
<Breadcrumbs size="M">
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
</Breadcrumbs>

Large (default)

tsx example
<Breadcrumbs size="L">
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
</Breadcrumbs>

Multiline

Use the isMultiline prop to place the last item below the other items.
This adds emphasis to the current location as a page title or heading.

tsx example
<Breadcrumbs isMultiline>
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
  <Item key="march 2020 assets">March 2020 Assets</Item>
</Breadcrumbs>

Root context

View guidelines

Some applications find that always displaying the root item is useful to orient users.
This variation keeps the root visible when other items are truncated into the menu.

tsx example
<View overflow="hidden" width="200px">
  <Breadcrumbs showRoot>
    <Item key="home">Home</Item>
    <Item key="trendy">Trendy</Item>
    <Item key="2020 assets">March 2020 Assets</Item>
    <Item key="winter">Winter</Item>
    <Item key="holiday">Holiday</Item>
  </Breadcrumbs>
</View>

Disabled

Breadcrumbs in a disabled state shows items, but indicates that navigation is not available.
This can be used to maintain layout continuity.

tsx example
<Breadcrumbs isDisabled>
  <Item key="home">Home</Item>
  <Item key="trendy">Trendy</Item>
  <Item key="march 2020 assets">March 2020 Assets</Item>
</Breadcrumbs>

Visible items (overflow behavior)

View guidelines

Breadcrumbs collapses items into a menu when space is limited. It will only show a maximum of 4 visible items including the root and menu button, if either are visible. If the root item cannot be rendered in the available horizontal
space, it will be collapsed into the menu regardless of showRoot. Note that the last breadcrumb item will automatically
truncate with an ellipsis instead of collapsing into the menu.

Resize your browser window to see the above behavior in the examples below.

tsx example
<Breadcrumbs>
  <Item key="shared">My Shared Documents</Item>
  <Item key="catalogue">North America Spring Catalogue</Item>
  <Item key="march 2020">March 2020</Item>
  <Item key="assets">Downloaded Screenshots and Assets (approval required)</Item>
  <Item key="streetwear">Streetwear</Item>
  <Item key="jackets">Jackets</Item>
</Breadcrumbs>
tsx example
<Breadcrumbs showRoot>
  <Item key="shared">My Shared Documents</Item>
  <Item key="catalogue">North America Spring Catalogue</Item>
  <Item key="march 2020">March 2020</Item>
  <Item key="assets">Downloaded Screenshots and Assets (approval required)</Item>
</Breadcrumbs>

Packages @Adobe React Spectrum Docs Button ActionButton (`packages/@adobe/react-spectrum/docs/button/ActionButton.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/button';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/button/package.json';

jsx import
import {ActionButton} from '@react-spectrum/button';
import {View} from '@react-spectrum/view';
import {Flex} from '@react-spectrum/layout';

category: Buttons keywords: [action button]

ActionButton

{docs.exports.ActionButton.description}

<HeaderInfo
packageData={packageData}
componentNames={['ActionButton']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/action-button/'}
]}
since="3.0.0" />

Example

tsx example
<ActionButton>Edit</ActionButton>

Content

ActionButtons can have a label, an icon, or both. An icon is provided by passing an icon component as a child to the ActionButton.
A visible label can be provided by passing a string or a Text component as a child, depending on whether the ActionButton has an accompanying icon.

tsx example
import {Text} from '@react-spectrum/text';
import Edit from '@spectrum-icons/workflow/Edit';

<ActionButton>
  <Edit />
  <Text>Icon + Label</Text>
</ActionButton>

Accessibility

If no visible label is provided (e.g. an icon only button),
an alternative text label must be provided to identify the control for accessibility. This should be added using
the aria-label prop.

tsx example
<ActionButton aria-label="Icon only">
  <Edit />
</ActionButton>

Internationalization

In order to internationalize an ActionButton, a localized string should be passed to the children or aria-label prop.

Events

ActionButtons support user interactions via mouse, keyboard, and touch. You can handle all of these via the onPress
prop.

The following example uses an onPress handler to update a counter stored in React state.

tsx example
function Example() {
  let [count, setCount] = React.useState(0);

  return (
   <ActionButton onPress={() => setCount(c => c + 1)}>{count} Edits</ActionButton>
  );
}

Props

Visual options

Quiet

View guidelines

tsx example
<ActionButton isQuiet>Action!</ActionButton>

Disabled

View guidelines

tsx example
<ActionButton isDisabled>Action!</ActionButton>

Static color

The staticColor prop can be used when an ActionButton is displayed over a color background. You are responsible for
choosing the static color variant that ensures the text meets an
accessible contrast ratio
with the background.

tsx example
<Flex wrap gap="size-250">
  <View backgroundColor="static-blue-700" padding="size-500">
    <ActionButton staticColor="white">
      <Edit />
      <Text>Edit</Text>
    </ActionButton>
  </View>
  <View backgroundColor="static-yellow-400" padding="size-500">
    <ActionButton staticColor="black" isQuiet>
      <Edit />
      <Text>Edit</Text>
    </ActionButton>
  </View>
</Flex>

Packages @Adobe React Spectrum Docs Button Button (`packages/@adobe/react-spectrum/docs/button/Button.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/button';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/button/package.json';

jsx import
import {Button} from '@react-spectrum/button';
import Bell from '@spectrum-icons/workflow/Bell';
import {View} from '@react-spectrum/view';
import {Flex} from '@react-spectrum/layout';

category: Buttons

Button

{docs.exports.Button.description}

<HeaderInfo
packageData={packageData}
componentNames={['Button']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/button/'}
]}
since="3.0.0" />

Example

tsx example
<Button variant="accent">Save</Button>

Content

Buttons must have a visible label, and can optionally have an icon. Text only buttons accept a string
as children. Icons can also be added as children, with a sibling Text element for the label.

tsx example
import {Text} from '@react-spectrum/text';

<Button variant="primary">
  <Bell />
  <Text>Icon + Label</Text>
</Button>

Accessibility

If no visible label is provided (e.g. an icon only button),
an alternative text label must be provided to identify the control for accessibility. This should be added using
the aria-label prop.

Internationalization

In order to internationalize a button, a localized string should be passed to the children or aria-label prop.

Events

Buttons support user interactions via mouse, keyboard, and touch. You can handle all of these via the onPress
prop.

The following example uses an onPress handler to update a counter stored in React state.

tsx example
function Example() {
  let [count, setCount] = React.useState(0);

  return (
    <Button variant="primary" onPress={() => setCount(c => c + 1)}>{count} Dogs</Button>
  );
}

Pending

Buttons can indicate that a quick progress task is taking place (e.g., saving settings on a server). After a 1 second delay, an indeterminate spinner will be displayed in place of the button label and icon. You can trigger this behavior with the isPending prop. Button events are disabled while isPending is true.

tsx example
function Example() {
  let [isLoading, setIsLoading] = React.useState(false);

  let handlePress = () => {
    // Trigger button pending state
    setIsLoading(true);

    setTimeout(() => {
      // Cancel button pending state
      setIsLoading(false);
    }, 3000);
  };

  return (
    <Button variant="primary" isPending={isLoading} onPress={handlePress}>Click me!</Button>
  );
}

Props

Visual options

Accent

View guidelines

tsx example
<Flex wrap gap="size-250">
  <Button variant="accent" style="fill">Save</Button>
  <Button variant="accent" style="outline">Save</Button>
</Flex>

Primary

View guidelines

tsx example
<Flex wrap gap="size-250">
  <Button variant="primary" style="fill">Save</Button>
  <Button variant="primary" style="outline">Save</Button>
</Flex>

Secondary

View guidelines

tsx example
<Flex wrap gap="size-250">
  <Button variant="secondary" style="fill">Save</Button>
  <Button variant="secondary" style="outline">Save</Button>
</Flex>

Negative

View guidelines

tsx example
<Flex wrap gap="size-250">
  <Button variant="negative" style="fill">Save</Button>
  <Button variant="negative" style="outline">Save</Button>
</Flex>

Static color

View guidelines

tsx example
<Flex wrap gap="size-250">
  <View backgroundColor="static-blue-700" padding="size-500">
    <Flex wrap gap="size-200">
      <Button variant="primary" staticColor="white" style="fill">Save</Button>
      <Button variant="primary" staticColor="white" style="outline">Save</Button>
    </Flex>
  </View>
  <View backgroundColor="static-yellow-400" padding="size-500">
    <Flex wrap gap="size-200">
      <Button variant="primary" staticColor="black" style="fill">Save</Button>
      <Button variant="primary" staticColor="black" style="outline">Save</Button>
    </Flex>
  </View>
</Flex>

Disabled

View guidelines

tsx example
<Button variant="accent" isDisabled>Save</Button>

Icon only

View guidelines

tsx example
<Flex direction="row" gap={8}>
  <Button variant="accent" aria-label="Ring for service"><Bell /></Button>
  <Button variant="primary" aria-label="Ring for service"><Bell /></Button>
  <Button variant="secondary" aria-label="Ring for service"><Bell /></Button>
</Flex>

Packages @Adobe React Spectrum Docs Button LogicButton (`packages/@adobe/react-spectrum/docs/button/LogicButton.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/button';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/button/package.json';

jsx import
import {LogicButton} from '@react-spectrum/button';

category: Buttons keywords: [logic button]

LogicButton

{docs.exports.LogicButton.description}

<HeaderInfo
packageData={packageData}
componentNames={['LogicButton']}
since="3.0.0" />

Example

tsx example
<LogicButton variant="and">And</LogicButton>

Content

LogicButtons must have a variant and optionally can have a label.
A visible label can be provided by passing children.

tsx example
<LogicButton variant="or">Or</LogicButton>

Internationalization

In order to internationalize a LogicButton, a localized string should be passed to the children or aria-label prop.

Events

LogicButton supports user interactions via mouse, keyboard, and touch. You can handle all of these via the onPress prop.

The following example uses an onPress handler to update the variant.

tsx example
function Example() {
  let [variant, setVariant] = React.useState<'and' | 'or'>('or');

  return (
    <LogicButton variant={variant} onPress={() => setVariant(variant === 'or' ? 'and' : 'or')}>{variant}</LogicButton>
  );
}

Props

Visual options

Variant

tsx example
<LogicButton variant="or" marginEnd="20px">Or</LogicButton>
<LogicButton variant="and">And</LogicButton>

Disabled

tsx example
<LogicButton variant="or" isDisabled>Or</LogicButton>

Packages @Adobe React Spectrum Docs Button ToggleButton (`packages/@adobe/react-spectrum/docs/button/ToggleButton.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/button';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/button/package.json';

jsx import
import {Flex} from '@react-spectrum/layout';
import {ToggleButton} from '@react-spectrum/button';
import {View} from '@react-spectrum/view';

category: Buttons keywords: [toggle button]

ToggleButton

{docs.exports.ToggleButton.description}

<HeaderInfo
packageData={packageData}
componentNames={['ToggleButton']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/action-button/'}
]}
since="3.2.0" />

Example

tsx example
<ToggleButton>Pin</ToggleButton>

Content

ToggleButtons can have a label, an icon, or both. An icon is provided by passing an icon component as a child to the ToggleButton.
A visible label can be provided by passing a string or a Text component as a child, depending on whether the ToggleButton has an accompanying icon.

tsx example
import {Text} from '@react-spectrum/text';
import Pin from '@spectrum-icons/workflow/PinOff';

<ToggleButton>
  <Pin />
  <Text>Icon + Label</Text>
</ToggleButton>

Accessibility

If no visible label is provided (e.g. an icon only button),
an alternative text label must be provided to identify the control for accessibility. This should be added using
the aria-label prop.

tsx example
<ToggleButton aria-label="Icon only">
  <Pin />
</ToggleButton>

Note: ToggleButton should not be used when the content of the button changes between selection states, for example,
mute/unmute or play/pause. In these cases, use an ActionButton instead.

Internationalization

In order to internationalize an ToggleButton, a localized string should be passed to the children or aria-label prop.

Value

ToggleButtons are not selected by default. The defaultSelected prop can be used to set the default state (uncontrolled).
Alternatively, the isSelected prop can be used to make the selected state controlled. See React's documentation on
uncontrolled components for more info, and Events, below,
for an example of controlled behavior.

Events

ToggleButtons support user interactions via mouse, keyboard, and touch. When pressing the button, the selection
state is toggled, and the onChange event is fired. The following example uses an onChange handler to update React state.

tsx example
function Example() {
  let [isSelected, setSelected] = React.useState(false);

  return (
    <ToggleButton
      isEmphasized
      isSelected={isSelected}
      onChange={setSelected}
      aria-label="Pin">
      <Pin />
    </ToggleButton>
  );
}

Props

Visual options

Quiet

View guidelines

tsx example
<ToggleButton isQuiet>Pin</ToggleButton>

Disabled

View guidelines

tsx example
<ToggleButton isDisabled>Pin</ToggleButton>

Emphasized

View guidelines

tsx example
<ToggleButton isEmphasized defaultSelected>Pin</ToggleButton>

Static color

The staticColor prop can be used when a ToggleButton is displayed over a color background. When selected, the icon and text
automatically take on the color of the background. You are responsible for choosing the static color variant that ensures the
text meets an accessible contrast ratio
with the background.

tsx example
<Flex wrap gap="size-250">
  <View backgroundColor="static-blue-700" padding="size-500">
    <ToggleButton staticColor="white">
      <Pin />
      <Text>Pin</Text>
    </ToggleButton>
  </View>
  <View backgroundColor="static-yellow-400" padding="size-500">
    <ToggleButton staticColor="black" isQuiet defaultSelected>
      <Pin />
      <Text>Pin</Text>
    </ToggleButton>
  </View>
</Flex>

Packages @Adobe React Spectrum Docs Buttongroup ButtonGroup (`packages/@adobe/react-spectrum/docs/buttongroup/ButtonGroup.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/buttongroup';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/buttongroup/package.json';


category: Buttons keywords: [button group]

ButtonGroup

{docs.exports.ButtonGroup.description}

<HeaderInfo
packageData={packageData}
componentNames={['ButtonGroup']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/button-group/'}
]}
since="3.0.0" />

Example

tsx example
import {ButtonGroup} from '@react-spectrum/buttongroup';
import {Button} from '@react-spectrum/button';

<ButtonGroup>
  <Button variant="primary">Rate Now</Button>
  <Button variant="secondary">No, thanks</Button>
  <Button variant="secondary">Remind me later</Button>
</ButtonGroup>

Content

ButtonGroup expects Buttons as children and is used to handle overflow. When horizontal space is limited, the ButtonGroup switches to a vertical layout. ButtonGroup should be used when there are two or more related Buttons in a group.
To see the orientation switch in the example above, resize the width of your window.

Props

Visual options

Orientation

View guidelines

Setting this to 'vertical' will prevent any dynamic orientation changes, the ButtonGroup will remain in vertical regardless of the available width.

tsx example
<ButtonGroup orientation="vertical">
  <Button variant="secondary">No, thanks</Button>
  <Button variant="secondary">Remind me later</Button>
  <Button variant="primary">Rate Now</Button>
</ButtonGroup>

Alignment

View guidelines

Button groups are aligned contextually. Generally, button groups should be start-aligned to follow content. However, they should be end-aligned inside container components, such as in dialogs, popovers, or cards. Center alignment should be used in the context of an empty state. In right-to-left languages, alignment is flipped automatically.

tsx example
<ButtonGroup orientation="vertical" align="end">
  <Button variant="secondary">No, thanks</Button>
  <Button variant="secondary">Remind me later</Button>
  <Button variant="primary">Rate Now</Button>
</ButtonGroup>
tsx example
<ButtonGroup orientation="vertical" align="center">
  <Button variant="secondary">No, thanks</Button>
  <Button variant="secondary">Remind me later</Button>
  <Button variant="primary">Rate Now</Button>
</ButtonGroup>

Disabled

View guidelines

tsx example
<ButtonGroup isDisabled>
  <Button variant="secondary">No, thanks</Button>
  <Button variant="secondary">Remind me later</Button>
  <Button variant="primary">Rate Now</Button>
</ButtonGroup>

Packages @Adobe React Spectrum Docs Calendar Calendar (`packages/@adobe/react-spectrum/docs/calendar/Calendar.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/calendar';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/calendar/package.json';

jsx import
import {Calendar} from '@react-spectrum/calendar';
import {Flex} from '@react-spectrum/layout';
import {ActionButton} from '@adobe/react-spectrum';

category: Date and Time keywords: [date]

Calendar

{docs.exports.Calendar.description}

<HeaderInfo
packageData={packageData}
componentNames={['Calendar']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<Calendar aria-label="Event date" />

Value

A Calendar has no selection by default. An initial, uncontrolled value can be provided to the Calendar using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Date values are provided using objects in the @internationalized/date package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns.

Calendar supports values with both date and time components, but only allows users to modify the date. By default, Calendar will emit objects in the onChange event, but if a or object is passed as the value or defaultValue, values of that type will be emitted, changing only the date and preserving the time components.

tsx example
import {parseDate} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState(parseDate('2020-02-03'));

  return (
    <Flex gap="size-300" wrap>
      <Calendar
        aria-label="Date (uncontrolled)"
        defaultValue={parseDate('2020-02-03')} />
      <Calendar
        aria-label="Date (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

International calendars

Calendar supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the Unicode calendar locale extension, passed to the Provider component.

Selected dates passed to onChange always use the same calendar system as the value or defaultValue prop. If no value or defaultValue is provided, then dates passed to onChange are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.

The below example displays a Calendar in the Hindi language, using the Indian calendar. Dates emitted from onChange are in the Gregorian calendar.

tsx example
import {Provider} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(null);
  return (
    <Provider locale="hi-IN-u-ca-indian">
      <Calendar aria-label="Date" value={date} onChange={setDate} />
      <p>Selected date: {date?.toString()}</p>
    </Provider>
  );
}

Custom calendar systems

Calendar also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a 4-5-4 format, where month ranges don't follow the usual Gregorian calendar.

The createCalendar prop accepts a function that returns an instance of the interface. See the @internationalized/date docs for an example implementation.

tsx import
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';
text
/* Detailed source-code truncated for AI context efficiency. */

Labeling

An aria-label must be provided to the Calendar for accessibility. If it is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Internationalization

In order to internationalize a Calendar, a localized string should be passed to the aria-label prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the Calendar is automatically flipped. Dates are automatically formatted using the current locale.

Events

Calendar accepts an onChange prop which is triggered whenever a date is selected by the user. The example below uses onChange to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript Date object to pass to the formatter.

tsx example
import {getLocalTimeZone} from '@internationalized/date';
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(parseDate('2022-07-04'));
  let formatter = useDateFormatter({dateStyle: 'full'});

  return (
    <>
      <Calendar aria-label="Event date" value={date} onChange={setDate} />
      <p>Selected date: {formatter.format(date.toDate(getLocalTimeZone()))}</p>
    </>
  );
}

Validation

By default, Calendar allows selecting any date. The minValue and maxValue props can also be used to prevent the user from selecting dates outside a certain range.

This example only accepts dates after today.

tsx example
import {today} from '@internationalized/date';

<Calendar aria-label="Appointment date" minValue={today(getLocalTimeZone())} />

Unavailable dates

Calendar supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance. The isDateUnavailable prop accepts a callback that is called to evaluate whether each visible date is unavailable.

This example includes multiple unavailable date ranges, e.g. dates when no appointments are available. In addition, all weekends are unavailable. The minValue prop is also used to prevent selecting dates before today.

tsx example
import {today, isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let now = today(getLocalTimeZone());
  let disabledRanges = [
    [now, now.add({days: 5})],
    [now.add({days: 14}), now.add({days: 16})],
    [now.add({days: 23}), now.add({days: 24})],
  ];

  let {locale} = useLocale();
  let isDateUnavailable = (date) => isWeekend(date, locale) || disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0);

  return <Calendar aria-label="Appointment date" minValue={today(getLocalTimeZone())} isDateUnavailable={isDateUnavailable} />
}

Controlling the focused date

By default, the selected date is focused when a Calendar first mounts. If no value or defaultValue prop is provided, then the current date is focused. However, Calendar supports controlling which date is focused using the focusedValue and onFocusChange props. This also determines which month is visible. The defaultFocusedValue prop allows setting the initial focused date when the Calendar first mounts, without controlling it.

This example focuses July 1, 2021 by default. The user may change the focused date, and the onFocusChange event updates the state. Clicking the button resets the focused date back to the initial value.

tsx example
import {CalendarDate} from '@internationalized/date';

function Example() {
  let defaultDate = new CalendarDate(2021, 7, 1);
  let [focusedDate, setFocusedDate] = React.useState(defaultDate);

  return (
    <Flex direction="column" alignItems="start" gap="size-200">
      <ActionButton onPress={() => setFocusedDate(defaultDate)}>Reset focused date</ActionButton>
      <Calendar focusedValue={focusedDate} onFocusChange={setFocusedDate} />
    </Flex>
  );
}

Props

Visual options

Disabled

tsx example
<Calendar aria-label="Event date" isDisabled />

Read only

The isReadOnly boolean prop makes the Calendar's value immutable. Unlike isDisabled, the Calendar remains focusable.

tsx example
<Calendar aria-label="Event date" value={today(getLocalTimeZone())} isReadOnly />

Visible months

By default, the Calendar displays a single month. The visibleMonths prop allows displaying up to 3 months at a time.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <Calendar aria-label="Event date" visibleMonths={3} />
</div>

Page behavior

By default, when pressing the next or previous buttons, pagination will advance by the visibleMonths value. This behavior can be changed to page by single months instead, by setting pageBehavior to single.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <Calendar aria-label="Event date" visibleMonths={3} pageBehavior="single" />
</div>

Custom first day of week

By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the firstDayOfWeek prop to 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <Calendar aria-label="Event date" firstDayOfWeek="mon" />
</div>

Packages @Adobe React Spectrum Docs Calendar RangeCalendar (`packages/@adobe/react-spectrum/docs/calendar/RangeCalendar.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/calendar';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/calendar/package.json';

jsx import
import {RangeCalendar} from '@react-spectrum/calendar';
import {Flex} from '@react-spectrum/layout';
import {ActionButton} from '@adobe/react-spectrum';

category: Date and Time keywords: [date]

RangeCalendar

{docs.exports.RangeCalendar.description}

<HeaderInfo
packageData={packageData}
componentNames={['RangeCalendar']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<RangeCalendar aria-label="Trip dates" />

Value

A RangeCalendar has no selection by default. An initial, uncontrolled value can be provided to the RangeCalendar using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Date ranges are objects with start and end properties containing date values, which are provided using objects in the @internationalized/date package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns.

RangeCalendar supports values with both date and time components, but only allows users to modify the dates. By default, RangeCalendar will emit objects in the onChange event, but if a or object is passed as the value or defaultValue, values of that type will be emitted, changing only the date and preserving the time components.

tsx example
import {parseDate} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState({
    start: parseDate('2020-02-03'),
    end: parseDate('2020-02-12')
  });

  return (
    <Flex gap="size-300" wrap>
      <RangeCalendar
        aria-label="Date range (uncontrolled)"
        defaultValue={{
          start: parseDate('2020-02-03'),
          end: parseDate('2020-02-12')
        }} />
      <RangeCalendar
        aria-label="Date range (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

International calendars

RangeCalendar supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the Unicode calendar locale extension, passed to the Provider component.

Selected dates passed to onChange always use the same calendar system as the value or defaultValue prop. If no value or defaultValue is provided, then dates passed to onChange are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.

The below example displays a RangeCalendar in the Hindi language, using the Indian calendar. Dates emitted from onChange are in the Gregorian calendar.

tsx example
import {Provider} from '@adobe/react-spectrum';

function Example() {
  let [range, setRange] = React.useState(null);
  return (
    <Provider locale="hi-IN-u-ca-indian">
      <RangeCalendar aria-label="Date range" value={range} onChange={setRange} />
      <p>Start date: {range?.start.toString()}</p>
      <p>End date: {range?.end.toString()}</p>
    </Provider>
  );
}

Custom calendar systems

RangeCalendar also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a 4-5-4 format, where month ranges don't follow the usual Gregorian calendar.

The createCalendar prop accepts a function that returns an instance of the interface. See the @internationalized/date docs for an example implementation.

tsx import
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';
text
/* Detailed source-code truncated for AI context efficiency. */

Labeling

An aria-label must be provided to the RangeCalendar for accessibility. If it is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Internationalization

In order to internationalize a RangeCalendar, a localized string should be passed to the aria-label prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the RangeCalendar is automatically flipped. Dates are automatically formatted using the current locale.

Events

RangeCalendar accepts an onChange prop which is triggered whenever a date is selected by the user. The example below uses onChange to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript Date object to pass to the formatter.

tsx example
import {getLocalTimeZone} from '@internationalized/date';
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [range, setRange] = React.useState({
    start: parseDate('2020-07-03'),
    end: parseDate('2020-07-10')
  });
  let formatter = useDateFormatter({dateStyle: 'long'});

  return (
    <>
      <RangeCalendar aria-label="Date range" value={range} onChange={setRange} />
      <p>
        Selected date:{' '}
        {formatter.formatRange(
          range.start.toDate(getLocalTimeZone()),
          range.end.toDate(getLocalTimeZone())
        )}
      </p>
    </>
  );
}

Validation

By default, RangeCalendar allows selecting any date range. The minValue and maxValue props can also be used to prevent the user from selecting dates outside a certain range.

This example only accepts dates after today.

tsx example
import {today} from '@internationalized/date';

<RangeCalendar aria-label="Trip dates" minValue={today(getLocalTimeZone())} />

Unavailable dates

RangeCalendar supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance. The isDateUnavailable prop accepts a callback that is called to evaluate whether each visible date is unavailable.

Note that by default, users may not select non-contiguous ranges, i.e. ranges that contain unavailable dates within them. Once a start date is selected, enabled dates will be restricted to subsequent dates until an unavailable date is hit. See below for an example of how to allow non-contiguous ranges.

This example includes multiple unavailable date ranges, e.g. dates when a rental house is not available. The minValue prop is also used to prevent selecting dates before today.

tsx example
import {today} from '@internationalized/date';

function Example() {
  let now = today(getLocalTimeZone());
  let disabledRanges = [
    [now, now.add({days: 5})],
    [now.add({days: 14}), now.add({days: 16})],
    [now.add({days: 23}), now.add({days: 24})],
  ];

  let isDateUnavailable = (date) => disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0);

  return <RangeCalendar aria-label="Trip dates" minValue={today(getLocalTimeZone())} isDateUnavailable={isDateUnavailable} />
}

Non-contiguous ranges

The allowsNonContiguousRanges prop enables a range to be selected even if there are unavailable dates in the middle. The value emitted in the onChange event will still be a single range with a start and end property, but unavailable dates will not be displayed as selected. It is up to applications to split the full selected range into multiple as needed for business logic.

This example prevents selecting weekends, but allows selecting ranges that span multiple weeks.

tsx example
import {isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let {locale} = useLocale();

  return <RangeCalendar aria-label="Time off request" isDateUnavailable={date => isWeekend(date, locale)} allowsNonContiguousRanges />
}

Controlling the focused date

By default, the first selected date is focused when a RangeCalendar first mounts. If no value or defaultValue prop is provided, then the current date is focused. However, RangeCalendar supports controlling which date is focused using the focusedValue and onFocusChange props. This also determines which month is visible. The defaultFocusedValue prop allows setting the initial focused date when the RangeCalendar first mounts, without controlling it.

This example focuses July 1, 2021 by default. The user may change the focused date, and the onFocusChange event updates the state. Clicking the button resets the focused date back to the initial value.

tsx example
import {CalendarDate} from '@internationalized/date';

function Example() {
  let defaultDate = new CalendarDate(2021, 7, 1);
  let [focusedDate, setFocusedDate] = React.useState(defaultDate);

  return (
    <Flex direction="column" alignItems="start" gap="size-200">
      <ActionButton onPress={() => setFocusedDate(defaultDate)}>Reset focused date</ActionButton>
      <RangeCalendar focusedValue={focusedDate} onFocusChange={setFocusedDate} />
    </Flex>
  );
}

Props

Visual options

Disabled

tsx example
<RangeCalendar aria-label="Trip dates" isDisabled />

Read only

The isReadOnly boolean prop makes the RangeCalendar's value immutable. Unlike isDisabled, the RangeCalendar remains focusable.

tsx example
<RangeCalendar aria-label="Trip dates" value={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({ weeks: 1 })}} isReadOnly />

Visible months

By default, a RangeCalendar displays a single month. The visibleMonths prop allows displaying up to 3 months at a time.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <RangeCalendar aria-label="Trip dates" visibleMonths={3} />
</div>

Page behavior

By default, when pressing the next or previous buttons, pagination will advance by the visibleMonths value. This behavior can be changed to page by single months instead, by setting pageBehavior to single.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <RangeCalendar aria-label="Trip dates" visibleMonths={3} pageBehavior="single" />
</div>

Custom first day of week

By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the firstDayOfWeek prop to 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.

tsx example
<div style={{maxWidth: '100%', overflow: 'auto'}}>
  <RangeCalendar aria-label="Trip dates" firstDayOfWeek="mon" />
</div>

Packages @Adobe React Spectrum Docs Checkbox Checkbox (`packages/@adobe/react-spectrum/docs/checkbox/Checkbox.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/checkbox';
import packageData from '@react-spectrum/checkbox/package.json';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';

jsx import
import {Checkbox} from '@react-spectrum/checkbox';
 import {View} from '@react-spectrum/view';
 import {Flex} from '@react-spectrum/layout';

category: Forms keywords: [input]

Checkbox

{docs.exports.Checkbox.description}

<HeaderInfo
packageData={packageData}
componentNames={['Checkbox']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/checkbox/'}
]}
since="3.0.0" />

Example

tsx example
<Checkbox>Unsubscribe</Checkbox>

Content

Checkboxes accept children, which are rendered as the label.

Accessibility

In certain cases a visible label isn't needed. For example, a checkbox used to select a table row.
If a visible label isn't specified, an aria-label must be provided to the Checkbox for accessibility.
If the field is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Internationalization

To internationalize a Checkbox, a localized label should be passed to the children or aria-label prop.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the checkbox is automatically flipped.

Value

Checkboxes are not selected by default. The defaultSelected prop can be used to set the default state (uncontrolled).
Alternatively, the isSelected prop can be used to make the selected state controlled. See React's documentation on uncontrolled components for more info.

tsx example
function Example() {
  let [selected, setSelected] = React.useState(true);

  return (
    <Flex direction="row">
      <Checkbox defaultSelected>Subscribe (uncontrolled)</Checkbox>
      <Checkbox isSelected={selected} onChange={setSelected}>Subscribe (controlled)</Checkbox>
    </Flex>
  );
}

Indeterminate

View guidelines

A Checkbox can be in an indeterminate state, controlled using the isIndeterminate prop.
This overrides the appearance of the Checkbox, whether selection is controlled or uncontrolled.
The Checkbox will visually remain indeterminate until the isIndeterminate prop is set to false, regardless of user interaction.

tsx example
<Checkbox isIndeterminate>Subscribe</Checkbox>

HTML forms

Checkbox supports the name and value props for integration with HTML forms.

tsx example
<Checkbox name="newsletter" value="subscribe">Subscribe</Checkbox>

Events

Checkboxes accept a user defined onChange prop, triggered whenever the Checkbox is clicked.
The example below uses onChange to alert the user to changes in the checkbox's state.

tsx example
function Example() {
  let [selected, setSelection] = React.useState(false);

  return (
    <Flex direction="column">
      <Checkbox isSelected={selected} onChange={setSelection}>
        Subscribe
      </Checkbox>
      <View>{`You are ${selected ? 'subscribed' : 'unsubscribed'}`}</View>
    </Flex>
  );
 }

Validation

Checkboxes can display a validation state to communicate to the user if the current value is invalid.
Implement your own validation logic in your app and set the isInvalid prop accordingly.
The invalid state is only communicated via a color change so in order to be accessible the validation error must also be communicated in another way.

tsx example
<Checkbox isInvalid>I accept the terms and conditions</Checkbox>

Props

Visual options

Disabled

View guidelines

tsx example
<Checkbox isDisabled>Subscribe</Checkbox>

Emphasized

View guidelines

tsx example
<Checkbox isEmphasized defaultSelected>Subscribe</Checkbox>

Packages @Adobe React Spectrum Docs Checkbox CheckboxGroup (`packages/@adobe/react-spectrum/docs/checkbox/CheckboxGroup.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/checkbox';
import packageData from '@react-spectrum/checkbox/package.json';
import {HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs';

jsx import
import {Checkbox, CheckboxGroup} from '@react-spectrum/checkbox';
 import {Flex} from '@react-spectrum/layout';

category: Forms keywords: [input, checkbox group, accessibility] after_version: 3.1.0

CheckboxGroup

{docs.exports.CheckboxGroup.description}

<HeaderInfo
packageData={packageData}
componentNames={['CheckboxGroup', 'Checkbox']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/checkbox/'}
]}
since="3.0.0" />

Example

tsx example
<CheckboxGroup label="Favorite sports">
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Content

CheckboxGroup accepts multiple Checkbox elements as children.
Each Checkbox represents an option that can be selected, labeled by its children.

Value

CheckboxGroup supports selecting zero or more items. An initial, uncontrolled value can be provided to the CheckboxGroup using the defaultValue prop.
Alternatively, a controlled value can be provided using the value prop. Both of these props accept an array of selected items, which map to the
value prop on each item.

tsx example
function Example() {
  let [selected, setSelected] = React.useState(['soccer', 'baseball']);

  return (
    <Flex gap="size-300">
      <CheckboxGroup label="Favorite sports (uncontrolled)" defaultValue={['soccer', 'baseball']}>
        <Checkbox value="soccer">Soccer</Checkbox>
        <Checkbox value="baseball">Baseball</Checkbox>
        <Checkbox value="basketball">Basketball</Checkbox>
      </CheckboxGroup>

      <CheckboxGroup label="Favorite sports (controlled)" value={selected} onChange={setSelected}>
        <Checkbox value="soccer">Soccer</Checkbox>
        <Checkbox value="baseball">Baseball</Checkbox>
        <Checkbox value="basketball">Basketball</Checkbox>
      </CheckboxGroup>
    </Flex>
  );
}

HTML forms

CheckboxGroup supports the name prop, paired with the Checkbox value prop, for integration with HTML forms.

tsx example
<CheckboxGroup label="Condiments" name="condiments">
  <Checkbox value="mayo">Mayo</Checkbox>
  <Checkbox value="mustart">Mustard</Checkbox>
  <Checkbox value="ketchup">Ketchup</Checkbox>
</CheckboxGroup>

Labeling

A visual label should be provided for the CheckboxGroup using the label prop. If the CheckboxGroup requires an option to be selected by the user,
the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-300" wrap>
  <CheckboxGroup label="Favorite sports">
    <Checkbox value="soccer">Soccer</Checkbox>
    <Checkbox value="baseball">Baseball</Checkbox>
    <Checkbox value="basketball">Basketball</Checkbox>
  </CheckboxGroup>
  <CheckboxGroup label="Favorite sports" isRequired necessityIndicator="icon">
    <Checkbox value="soccer">Soccer</Checkbox>
    <Checkbox value="baseball">Baseball</Checkbox>
    <Checkbox value="basketball">Basketball</Checkbox>
  </CheckboxGroup>
  <CheckboxGroup label="Favorite sports" isRequired necessityIndicator="label">
    <Checkbox value="soccer">Soccer</Checkbox>
    <Checkbox value="baseball">Baseball</Checkbox>
    <Checkbox value="basketball">Basketball</Checkbox>
  </CheckboxGroup>
  <CheckboxGroup label="Favorite sports" necessityIndicator="label">
    <Checkbox value="soccer">Soccer</Checkbox>
    <Checkbox value="baseball">Baseball</Checkbox>
    <Checkbox value="basketball">Basketball</Checkbox>
  </CheckboxGroup>
</Flex>

Accessibility

If a visible label isn't specified for a CheckboxGroup, an aria-label must be provided for accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Checkbox elements within a group should always have a visible label.

Internationalization

To internationalize a CheckboxGroup, a localized string should be passed to the label prop and as the child content of the Checkbox elements.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the Checkbox is automatically placed on the right side of the text. When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Events

CheckboxGroup accepts an onChange prop, which is triggered when a user adds or removes an item from the selection.
The example below uses onChange to log how the user is interacting with the component.

tsx example
function Example() {
  let [selected, setSelected] = React.useState([]);

  return (
    <>
      <CheckboxGroup label="Favorite sports" value={selected} onChange={setSelected}>
        <Checkbox value="soccer">Soccer</Checkbox>
        <Checkbox value="baseball">Baseball</Checkbox>
        <Checkbox value="basketball">Basketball</Checkbox>
      </CheckboxGroup>
      <div>You have selected: {selected.join(', ')}</div>
    </>
  );
}

Validation

CheckboxGroup supports the isRequired prop to ensure the user selects at least one item, as well as custom client and server-side validation. Individual checkboxes also support validation, and errors from all checkboxes are aggregated at the group level. CheckboxGroup can also be integrated with other form libraries. See the Forms guide to learn more.

Group validation

The isRequired prop at the CheckboxGroup level requires that at least one item is selected. When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native">
  {/*- begin highlight -*/}
  <CheckboxGroup label="Sandwich condiments" name="condiments" isRequired>
  {/*- end highlight -*/}
    <Checkbox value="lettuce">Lettuce</Checkbox>
    <Checkbox value="tomato">Tomato</Checkbox>
    <Checkbox value="onion">Onion</Checkbox>
    <Checkbox value="sprouts">Sprouts</Checkbox>
  </CheckboxGroup>
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, CheckboxGroup displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Individual Checkbox validation

To require that specific checkboxes are checked, set the isRequired prop at the Checkbox level instead of the CheckboxGroup. The following example shows how to require that all items are selected.

tsx example
<Form validationBehavior="native">
  <CheckboxGroup label="Agree to the following" isRequired>
    {/*- begin highlight -*/}
    <Checkbox value="terms" isRequired>Terms and conditions</Checkbox>
    <Checkbox value="privacy" isRequired>Privacy policy</Checkbox>
    <Checkbox value="cookies" isRequired>Cookie policy</Checkbox>
    {/*- end highlight -*/}
  </CheckboxGroup>
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Props

Visual options

Orientation

CheckboxGroups are vertically oriented by default.
The orientation prop can be used to change the orientation to horizontal.

tsx example
<CheckboxGroup label="Favorite sports" orientation="horizontal">
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Label position and alignment

By default, the label is positioned above the CheckboxGroup.
The labelPosition prop can be used to position the label to the side. The labelAlign prop can
be used to align the label as "start" or "end".
For left-to-right (LTR) languages, "start" refers to the left most edge of the CheckboxGroup
and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<CheckboxGroup label="Favorite sports" labelPosition="side" labelAlign="end">
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Help text

View guidelines

Both a description and an error message can be supplied to a CheckboxGroup. The description is always visible unless the isInvalid is true and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
function Example() {
  let [checked, setChecked] = React.useState(['dogs', 'dragons']);
  let isValid = checked.length === 2 && checked.includes('dogs') && checked.includes('dragons');

  return (
    <CheckboxGroup
      label="Pets"
      onChange={setChecked}
      value={checked}
      isInvalid={!isValid}
      description="Select your pets."
      errorMessage={
        checked.includes('cats')
          ? 'No cats allowed.'
          : 'Select only dogs and dragons.'
      }>
      <Checkbox value="dogs">Dogs</Checkbox>
      <Checkbox value="cats">Cats</Checkbox>
      <Checkbox value="dragons">Dragons</Checkbox>
    </CheckboxGroup>
  );
}

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a CheckboxGroup.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<CheckboxGroup
  label="Favorite genres"
  contextualHelp={
    <ContextualHelp>
      <Heading>What does this do?</Heading>
      <Content>Your musical taste is used to train our machine learning recommendation algorithm.</Content>
    </ContextualHelp>
  }>
  <Checkbox value="rock">Rock</Checkbox>
  <Checkbox value="pop">Pop</Checkbox>
  <Checkbox value="classical">Classical</Checkbox>
</CheckboxGroup>

Disabled

View guidelines

tsx example
<CheckboxGroup label="Favorite sports" isDisabled>
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>
tsx example
<CheckboxGroup label="Favorite sports">
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball" isDisabled>Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Read only

View guidelines

The isReadOnly prop makes the selection immutable. Unlike isDisabled, the CheckboxGroup remains focusable.
See the MDN docs for more information.

tsx example
<CheckboxGroup label="Favorite sports" defaultValue={['baseball']} isReadOnly>
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Emphasized

View guidelines

tsx example
<CheckboxGroup label="Favorite sports" defaultValue={['soccer', 'baseball']} isEmphasized>
  <Checkbox value="soccer">Soccer</Checkbox>
  <Checkbox value="baseball">Baseball</Checkbox>
  <Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>

Packages @Adobe React Spectrum Docs Color ColorArea (`packages/@adobe/react-spectrum/docs/color/ColorArea.mdx`)

{/* Copyright 2020 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 colorTypes from 'docs:@react-stately/color';
import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorArea, ColorSlider, ColorWheel} from '@react-spectrum/color';
import {Flex, Grid} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';
import {View} from '@react-spectrum/view';

category: Color keywords: [color area]

ColorArea

{docs.exports.ColorArea.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorArea']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/color-area/'}
]}
since="3.35.0" />

Example

tsx example
<ColorArea defaultValue="#7f0000" />

Value

A ColorArea requires either an uncontrolled default value or a controlled value, provided using the defaultValue or value props respectively.
The value provided to either of these props should be a color string or object.
xChannel and yChannel props may also be provided to specify which color channels the color area should display, and the direction of each channel in the color gradient.
This must be one of the channels included in the color value, for example, for RGB colors, the "red", "green", and "blue" channels are available.
For a full list of supported channels, see the Props table below.

In the example below, the function is used to parse the initial color from a HSL string
so that value's type remains consistent.

tsx example
import {ColorArea} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';
import {parseColor} from '@react-stately/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('hsl(0, 100%, 50%)'));
  return (
    <Flex gap="size-300" wrap>
      <div>
        <Label id="hsl-uncontrolled-id">x: Saturation, y: Lightness (uncontrolled)</Label>
        <ColorArea
          aria-labelledby="hsl-uncontrolled-id"
          defaultValue={value}
          xChannel="saturation"
          yChannel="lightness" />
      </div>
      <div>
        <Label id="hsl-controlled-id">x: Saturation, y: Lightness (controlled)</Label>
        <ColorArea
          aria-labelledby="hsl-controlled-id"
          value={value}
          onChange={setValue}
          xChannel="saturation"
          yChannel="lightness" />
      </div>
    </Flex>
  );
}

HTML forms

ColorArea supports the xName and yName props for integration with HTML forms. The values will be submitted as numbers between the minimum and maximum value for the corresponding channel in the X and Y direction.

tsx example
<ColorArea xName="red" yName="green" />

Labeling

By default, ColorArea uses an aria-label for the localized string "Color Picker",
which labels the visually hidden `` elements for the two color channels, or on mobile devices,
the group containing them. If you wish to override this with a more specific label, an aria-label or
aria-labelledby prop may be passed to further identify the element to assistive technologies.
For example, for a ColorArea that adjusts a background color you might pass the aria-label prop,
"Background color", which ColorArea will return as the aria-label prop "Background color, Color picker". If you provide your own
aria-label or aria-labelledby, be sure to localize the string or labeling element appropriately.

Note that like ColorWheel, ColorArea does not include an integrated visible label or a label prop.
Use aria-labelledby to label the ColorArea using the IDREF assigned to a visible label.

tsx example
import {ColorArea} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';

<Flex gap="size-300" wrap alignItems="end">
  <ColorArea
    aria-label="Background color"
    defaultValue="hsl(0, 100%, 50%)"
    xChannel="saturation"
    yChannel="lightness" />
  <div>
    <Label
      id="hsl-aria-labelledby-id">Background color</Label>
    <ColorArea
      aria-labelledby="hsl-aria-labelledby-id"
      defaultValue="hsl(0, 100%, 50%)"
      xChannel="saturation"
      yChannel="lightness" />
  </div>
</Flex>

Accessibility

Role description

In order to communicate to a screen reader user that the ColorArea adjusts in two dimensions,
ColorArea provides an aria-roledescription, using the localized string "2D Slider", on each of the
visually hidden `` elements.

Value formatting

The aria-valuetext of each element within the ColorArea is formatted according to the user's locale automatically. It will include the localized color channel name and current value for each channel, with the channel name and value that the element controls coming before the channel name and value for the adjacent
element. For example, for an RGB ColorArea where the `xChannel` is "blue", the `yChannel` is "green", when the current selected color is yellow (`rgb(255, 255, 0)`), the representing the
blue channel will have aria-valuetext to announce as "Blue: 0, Green: 255", and the ``
representing the green channel will have aria-valuetext to announce as "Green: 255, Blue: 0".

Internationalization

For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ColorArea is automatically flipped. As mentioned previously,
ColorArea automatically uses the current locale to format the aria-valuetext for each of the `` element with the channel name and current value.

Events

ColorArea supports two events: onChange and onChangeEnd. onChange is triggered whenever the ColorArea's handle is dragged, and onChangeEnd
is triggered when the user stops dragging the handle. Both events receive a object
as a parameter.

The example below uses onChange and onChangeEnd to update two separate elements with the ColorArea's value.

tsx example
import {ColorArea} from '@react-spectrum/color';
import {parseColor} from '@react-stately/color';

function Example() {
  let [currentValue, setCurrentValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));
  let [finalValue, setFinalValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));

  return (
    <div>
      <ColorArea
        value={currentValue}
        onChange={setCurrentValue}
        onChangeEnd={setFinalValue} />
      <pre>Current value: {currentValue.toString('hsl')}</pre>
      <pre>Final value: {finalValue.toString('hsl')}</pre>
    </div>
  );
}

Creating a color picker

To build a fully functional color picker, combine a ColorArea, which adjusts two channels of a color value stored in state, with a separate control, like a ColorSlider or ColorWheel, for
adjusting the value of the third channel within the color space for the color value stored in state.
The current color space for a color can be returned using the color.getColorSpace method.
An array of channel names for a color can be returned using the color.getColorChannels method.
To get a localized channel name to use as a label, you can use the color.getChannelName method.

RGBA

This example shows how you could build an RGB color picker using a color area and color sliders bound to the same
color value in state. The
function is used to parse the initial color from a hex value, stored in state. The value and onChange props
of both the ColorArea and ColorSlider are used to make the components controlled, so that they both update when the
color is modified.

tsx example
import {ColorArea, ColorSlider} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';
import {parseColor} from '@react-stately/color';

function Example() {
  let [color, setColor] = React.useState(parseColor('#ff00ff'));
  let [redChannel, greenChannel, blueChannel] = color.getColorChannels();
  return (
    <fieldset style={{border: 0}}>
      <legend>{color.getColorSpace().toUpperCase()}A Example</legend>
      <Flex direction="column">
        <ColorArea xChannel={redChannel} yChannel={greenChannel} value={color} onChange={setColor} />
        <ColorSlider channel={blueChannel} value={color} onChange={setColor} />
        <ColorSlider channel="alpha" value={color} onChange={setColor} />
        <p>Current value: {color.toString('css')}</p>
      </Flex>
    </fieldset>
  );
}

HSLA

This example shows how to build a similar color picker to the one above, using HSLA colors instead,
and a ColorWheel instead of a ColorSlider for the Hue channel.

tsx example
import {ColorArea, ColorSlider, ColorWheel} from '@react-spectrum/color';
import {Flex, Grid} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';
import {parseColor} from '@react-stately/color';
import {View} from '@react-spectrum/view';

function Example() {
  let [color, setColor] = React.useState(parseColor('hsla(0, 100%, 50%, 0.5)'));
  let [, saturationChannel, lightnessChannel] = color.getColorChannels();
  return (
    <fieldset style={{border: 0}}>
      <legend>HSLA Example</legend>
      <Flex
        direction="column">
        <View
          position="relative"
          width="size-2400">
          <Grid
            position="absolute"
            justifyContent="center"
            alignContent="center"
            width="100%"
            height="100%">
            <ColorArea
              xChannel={saturationChannel}
              yChannel={lightnessChannel}
              value={color}
              onChange={setColor}
              size="size-1200" />
          </Grid>
          <ColorWheel
            value={color}
            onChange={setColor}
            size="size-2400" />
        </View>
        <ColorSlider channel="alpha" value={color} onChange={setColor} />
        <p>Current value: {color.toString('hsla')}</p>
      </Flex>
    </fieldset>
  );
}

HSBA

This example shows how to build an HSBA color picker.

tsx example
import {ColorArea, ColorSlider, ColorWheel} from '@react-spectrum/color';
import {Flex, Grid} from '@react-spectrum/layout';
import {Label} from '@react-spectrum/label';
import {parseColor} from '@react-stately/color';
import {View} from '@react-spectrum/view';

function Example() {
  let [color, setColor] = React.useState(parseColor('hsba(0, 100%, 50%, 0.5)'));
  let [, saturationChannel, brightnessChannel] = color.getColorChannels();
  return (
    <fieldset style={{border: 0}}>
      <legend>HSBA Example</legend>
      <Flex
        direction="column">
        <View
          position="relative"
          width="size-2400">
          <Grid
            position="absolute"
            justifyContent="center"
            alignContent="center"
            width="100%"
            height="100%">
            <ColorArea
              xChannel={saturationChannel}
              yChannel={brightnessChannel}
              value={color}
              onChange={setColor}
              size="size-1200" />
          </Grid>
          <ColorWheel
            value={color}
            onChange={setColor}
            size="size-2400" />
        </View>
        <ColorSlider channel="alpha" value={color} onChange={setColor} />
        <p>Current value: {color.toString('hsba')}</p>
      </Flex>
    </fieldset>
  );
}

Props

Visual options

Disabled

View guidelines

tsx example
import {ColorArea} from '@react-spectrum/color';

<ColorArea defaultValue="#7f0000" isDisabled />

Custom Size

View guidelines

tsx example
import {ColorArea} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

<Flex direction="column" gap="size-300">
  <ColorArea defaultValue="#7f0000" size="size-3600" maxWidth="100%" />
</Flex>

Testing

The ColorArea features a draggable handle that the user can interact with to change its color value.
Please see the following section in the testing docs for more information on how to simulate this action in your
test suite.

Simulating move event

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Color ColorField (`packages/@adobe/react-spectrum/docs/color/ColorField.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

jsx import
import {ColorField} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

category: Color keywords: [color field, input]

ColorField

{docs.exports.ColorField.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorField']}
sourceData={[]}
since="3.35.0" />

Example

tsx example
<ColorField label="Primary Color" />

Value

A ColorField's value is empty by default, but an initial, uncontrolled, value can be provided using the defaultValue prop.
Alternatively, a controlled value can be provided using the value prop. The value provided to either of these props should be a color string or object.

In the example below, the function is used to parse the initial color
from a hex value, stored in state.

tsx example
import {parseColor} from '@react-stately/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('#e73623'));

  return (
    <Flex gap="size-150" wrap>
      <ColorField
        label="Primary Color (Uncontrolled)"
        defaultValue="#e21" />

      <ColorField
        label="Primary Color (Controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

HTML forms

ColorField supports the name prop for integration with HTML forms. The value will be submitted to the server as a hex color string. When a channel prop is provided, the value will be submitted as a number instead.

tsx example
<ColorField label="Color" name="color" />

Color channel

By default, ColorField allows the user to edit the color as a hex value. When the colorSpace and channel props are provided, ColorField displays the value for that channel formatted as a number instead. Rendering multiple ColorFields together can allow a user to edit a color.

tsx example
function Example() {
  let [color, setColor] = React.useState(parseColor('#7f007f'));
  return (
    <>
      <div style={{display: 'flex', gap: 8}}>
        <ColorField label="Hue" value={color} onChange={setColor} colorSpace="hsl" channel="hue" />
        <ColorField label="Saturation" value={color} onChange={setColor} colorSpace="hsl" channel="saturation" />
        <ColorField label="Lightness" value={color} onChange={setColor} colorSpace="hsl" channel="lightness" />
      </div>
      <p>Current color value: {color?.toString('hex')}</p>
    </>
  );
}

Labeling

A visual label should be provided for the ColorField using the label prop. If the ColorField is required, the isRequired and
necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-150" wrap>
  <ColorField label="Primary Color" />
  <ColorField label="Primary Color" isRequired />
  <ColorField label="Primary Color" isRequired necessityIndicator="label" />
  <ColorField label="Primary Color" necessityIndicator="label" />
</Flex>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the ColorField for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a ColorField, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Validation

ColorField supports the isRequired prop to ensure the user enters a value, as well as custom validation functions, realtime validation, and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the color field or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <ColorField label="Color" name="color" isRequired />
  {/*- end highlight -*/}
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, ColorField displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Props

Visual options

Quiet

tsx example
<ColorField label="Primary Color" isQuiet />

Disabled

tsx example
<ColorField label="Primary Color" isDisabled defaultValue="#e73623" />

Read only

The isReadOnly boolean prop makes the ColorField's text content immutable. Unlike isDisabled, the ColorField remains focusable
and the contents can still be copied. See the MDN docs for more information.

tsx example
<ColorField label="Primary Color" isReadOnly defaultValue="#e73623" />

Label alignment and position

View guidelines

By default, the label is positioned above the ColorField. The labelPosition prop can be used to position the label to the side.
The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the
left most edge of the ColorField and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<ColorField label="Primary Color" labelPosition="side" labelAlign="end" />

Help text

View guidelines

Both a description and an error message can be supplied to a ColorField. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
<Flex gap="size-100" wrap>
  <ColorField label="Color" defaultValue="#abc" validationState="valid" description="Enter your favorite color." />
  <ColorField label="Color" validationState="invalid" errorMessage="Empty input is not allowed." />
</Flex>

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a ColorField.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<ColorField
  label="Accent Color"
  defaultValue="#e73623"
  contextualHelp={
    <ContextualHelp>
      <Heading>What is an accent color?</Heading>
      <Content>An accent color is the primary foreground color for your theme, used across all components.</Content>
    </ContextualHelp>
  } />

Custom width

View guidelines

tsx example
<ColorField label="Primary Color" width="size-3600" maxWidth="100%" />

Packages @Adobe React Spectrum Docs Color ColorPicker (`packages/@adobe/react-spectrum/docs/color/ColorPicker.mdx`)

{/* Copyright 2020 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 colorTypes from 'docs:@react-stately/color';
import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorPicker, ColorEditor} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

category: Color keywords: [color picker]

ColorPicker

{docs.exports.ColorPicker.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorPicker', 'ColorEditor']}
since="3.35.0" />

Example

tsx example
<ColorPicker label="Fill" defaultValue="#5100FF">
  <ColorEditor />
</ColorPicker>

Value

A ColorArea requires either an uncontrolled default value or a controlled value, provided using the defaultValue or value props respectively. The value provided to either of these props should be a color string or object.

In the example below, the function is used to parse the initial color from a HSL string so that value's type remains consistent.

tsx example
import {ColorPicker} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';
import {parseColor} from '@react-spectrum/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('hsl(25, 100%, 50%)'));
  return (
    <Flex gap="size-300" wrap>
      <ColorPicker
        label="Color Picker (uncontrolled)"
        /*- begin highlight -*/
        defaultValue="hsl(25, 100%, 50%)">
        {/*- end highlight -*/}
        <ColorEditor />
      </ColorPicker>
      <ColorPicker
        label="Color Picker (controlled)"
        /*- begin highlight -*/
        value={value}
        onChange={setValue}>
        {/*- end highlight -*/}
        <ColorEditor />
      </ColorPicker>
    </Flex>
  );
}

Labeling

A visual label should be provided for the ColorPicker using the label prop.

tsx example
<ColorPicker label="Stroke color" defaultValue="#345">
  <ColorEditor />
</ColorPicker>

Accessibility

If a visible label isn't specified, an aria-label should be provided to the ColorPicker for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

tsx example
<ColorPicker aria-label="Fill color" defaultValue="#184">
  <ColorEditor />
</ColorPicker>

In addition to the visible or accessibility label, a ColorPicker also announces a description of the current color value (e.g. "dark vibrant blue") as a part of the label.

Internationalization

For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ColorPicker is automatically flipped.

Events

ColorPicker accepts an onChange prop which is triggered whenever the value is edited by the user. It receives a object as a parameter.

The example below uses onChange to update a separate element with the color value as a string.

tsx example
import {parseColor} from '@react-spectrum/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));

  return (
    <div>
      <ColorPicker
        label="Color"
        value={value}
        onChange={setValue}>
        <ColorEditor />
      </ColorPicker>
      <p>Selected color: {value.toString('hsl')}</p>
    </div>
  );
}

Custom color editor

ColorEditor provides a default color picker layout including a color area for saturation and brightness, and color sliders for hue and alpha. It also includes ColorFields to view and enter an exact color value by number.

ColorPicker can be customized by providing any combination of color components as children, including ColorArea, ColorField, ColorSlider, ColorSwatchPicker, and ColorWheel.

tsx example
import {ColorWheel, ColorArea} from '@react-spectrum/color';

<ColorPicker label="Fill" defaultValue="#08f">
  <ColorWheel />
  <ColorArea
    colorSpace="hsb"
    xChannel="saturation"
    yChannel="brightness"
    size="size-400"
    position="absolute"
    top="calc(50% - size-400)"
    left="calc(50% - size-400)" />
</ColorPicker>

Props

ColorPicker

ColorEditor

Visual options

Hide alpha channel

By default, ColorEditor includes a ColorSlider and ColorField for editing the alpha channel. This can be hidden with the hideAlphaChannel prop.

tsx example
<ColorPicker label="Color" defaultValue="#f80">
  <ColorEditor hideAlphaChannel />
</ColorPicker>

Swatches

A ColorEditor can be combined with additional color components such as a ColorSwatchPicker to choose from a list of pre-defined colors.

tsx example
import {ColorSwatchPicker, ColorSwatch} from '@react-spectrum/color';

<ColorPicker label="Color" defaultValue="#A00">
  <Flex direction="column" gap="size-300">
    <ColorEditor />
    <ColorSwatchPicker>
      <ColorSwatch color="#A00" />
      <ColorSwatch color="#f80" />
      <ColorSwatch color="#080" />
      <ColorSwatch color="#08f" />
      <ColorSwatch color="#088" />
      <ColorSwatch color="#008" />
    </ColorSwatchPicker>
  </Flex>
</ColorPicker>

Channel sliders

This example uses ColorSlider to allow a user to adjust each channel of a color value, with a Picker to switch between color spaces.

tsx example
import type {ColorSpace} from '@react-spectrum/color';
import {ColorSlider, getColorChannels} from '@react-spectrum/color';
import {Picker, Item} from '@react-spectrum/picker';

function Example() {
  let [space, setSpace] = React.useState<ColorSpace>('rgb');

  return (
    <ColorPicker label="Color" defaultValue="#184">
      <Flex direction="column" gap="size-100">
        <Picker aria-label="Color space" isQuiet selectedKey={space} onSelectionChange={s => setSpace(s as ColorSpace)}>
          <Item key="rgb">RGB</Item>
          <Item key="hsl">HSL</Item>
          <Item key="hsb">HSB</Item>
        </Picker>
        {getColorChannels(space).map(channel => (
          <ColorSlider key={channel} colorSpace={space} channel={channel} />
        ))}
        <ColorSlider channel="alpha" />
      </Flex>
    </ColorPicker>
  );
}

Rounding

The rounding of the color swatch can be controlled with the rounding prop.

tsx example
<Flex direction="column" gap="size-100">
  <ColorPicker label="None" rounding="none" defaultValue="#A00">
    <ColorEditor />
  </ColorPicker>
  <ColorPicker label="Default" rounding="default" defaultValue="#080">
    <ColorEditor />
  </ColorPicker>
  <ColorPicker label="Full" rounding="full" defaultValue="#00F">
    <ColorEditor />
  </ColorPicker>
</Flex>

Size

The size of the color swatch can be controlled with the size prop. The default size is "S".

tsx example
<Flex direction="column" gap="size-100">
  <ColorPicker label="Extra small" size="XS" defaultValue="#A00">
    <ColorEditor />
  </ColorPicker>
  <ColorPicker label="Small" size="S" defaultValue="#080">
    <ColorEditor />
  </ColorPicker>
  <ColorPicker label="Medium" size="M" defaultValue="#FB0">
    <ColorEditor />
  </ColorPicker>
  <ColorPicker label="Large" size="L" defaultValue="#00F">
    <ColorEditor />
  </ColorPicker>
</Flex>

Packages @Adobe React Spectrum Docs Color ColorSlider (`packages/@adobe/react-spectrum/docs/color/ColorSlider.mdx`)

{/* Copyright 2020 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 colorTypes from 'docs:@react-stately/color';
import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorSlider} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

category: Color keywords: [color slider]

ColorSlider

{docs.exports.ColorSlider.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorSlider']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/color-slider/'}
]}
since="3.35.0" />

Example

tsx example
<ColorSlider defaultValue="#7f0000" channel="red" />

Value

A ColorSlider requires either an uncontrolled default value or a controlled value, provided using the defaultValue or value props respectively.
The value provided to either of these props should be a color string or object. The channel prop must also be provided to specify which color channel the slider should display. This must be one of the channels included in the color value, for example, for RGB colors, the "red", "green", and "blue" channels are available. For a full list of supported channels, see the Props table below.

In the example below, the function is used to parse the initial color from a HSL string
so that value's type remains consistent.

tsx example
import {parseColor} from '@react-stately/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('hsl(0, 100%, 50%)'));
  return (
    <Flex gap="size-300" wrap>
      <ColorSlider
        label="Hue (uncontrolled)"
        defaultValue="hsl(0, 100%, 50%)"
        channel="hue" />
      <ColorSlider
        label="Hue (controlled)"
        value={value}
        onChange={setValue}
        channel="hue" />
    </Flex>
  );
}

HTML forms

ColorSlider supports the name prop for integration with HTML forms. The value will be submitted as a number between the minimum and maximum value for the displayed channel.

tsx example
<ColorSlider
  defaultValue="#7f0000"
  channel="red"
  name="red" />

Labeling

The ColorSlider's channel name and value are shown above the ColorSlider by default. The label and value label can be hidden by providing
label={null} and showValueLabel={false} to the ColorSlider, respectively. In addition, a custom label can be provided via the label prop.

tsx example
<Flex gap="size-300" wrap alignItems="end">
  <ColorSlider channel="saturation" defaultValue="hsl(0, 100%, 50%)" label={null} />
  <ColorSlider channel="lightness" defaultValue="hsl(0, 100%, 50%)" showValueLabel={false} />
</Flex>

Accessibility

If the ColorSlider's label is hidden, a localized string for the channel name is used as the aria-label by default. The value of the ColorSlider
is also automatically localized and provided to the input as aria-valuetext regardless of the value label's visibility. If you provide your own aria-label
or aria-labelledby, be sure to localize the string or labeling element appropriately.

Internationalization

For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ColorSlider is automatically flipped. As mentioned previously,
ColorSlider automatically uses the current locale to format the channel and value label.

Events

ColorSlider supports two events: onChange and onChangeEnd. onChange is triggered whenever the ColorSlider's handle is dragged, and onChangeEnd
is triggered when the user stops dragging the handle. Both events receive a object
as a parameter.

The example below uses onChange and onChangeEnd to update two separate elements with the ColorSlider's value.

tsx example
function Example() {
  let [currentValue, setCurrentValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));
  let [finalValue, setFinalValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));

  return (
    <div>
      <ColorSlider
        value={currentValue}
        channel="hue"
        onChange={setCurrentValue}
        onChangeEnd={setFinalValue} />
      <pre>Current value: {currentValue.toString('hsl')}</pre>
      <pre>Final value: {finalValue.toString('hsl')}</pre>
    </div>
  );
}

Creating a color picker

RGBA

This example shows how you could build an RGBA color picker using four color sliders bound to the same
color value in state. The
function is used to parse the initial color from a hex value, stored in state. The value and onChange props
of ColorSlider are used to make the sliders controlled, so that they all update when the color is modified.

tsx example
function Example() {
  let [color, setColor] = React.useState(parseColor('#ff00ff'));

  return (
    <Flex direction="column">
      <ColorSlider channel="red" value={color} onChange={setColor} />
      <ColorSlider channel="green" value={color} onChange={setColor} />
      <ColorSlider channel="blue" value={color} onChange={setColor} />
      <ColorSlider channel="alpha" value={color} onChange={setColor} />
    </Flex>
  );
}

HSLA

This example shows how to build a similar color picker to the one above, using HSLA colors instead.

tsx example
function Example() {
  let [color, setColor] = React.useState(parseColor('hsla(0, 100%, 50%, 0.5)'));

  return (
    <Flex direction="column">
      <ColorSlider channel="hue" value={color} onChange={setColor} />
      <ColorSlider channel="saturation" value={color} onChange={setColor} />
      <ColorSlider channel="lightness" value={color} onChange={setColor} />
      <ColorSlider channel="alpha" value={color} onChange={setColor} />
    </Flex>
  );
}

HSBA

This example shows how to build an HSBA color picker.

tsx example
function Example() {
  let [color, setColor] = React.useState(parseColor('hsba(0, 100%, 50%, 0.5)'));
  return (
    <>
      <ColorSlider channel="hue" value={color} onChange={setColor} />
      <ColorSlider channel="saturation" value={color} onChange={setColor} />
      <ColorSlider channel="brightness" value={color} onChange={setColor} />
      <ColorSlider channel="alpha" value={color} onChange={setColor} />
    </>
  );
}

Props

Visual options

Disabled

View guidelines

tsx example
<ColorSlider defaultValue="#7f0000"  channel="red" isDisabled />

Vertical

View guidelines

tsx example
<ColorSlider defaultValue="#7f0000" channel="red" orientation="vertical" />

Custom Size

View guidelines

tsx example
<Flex direction="column" gap="size-300">
  <ColorSlider defaultValue="#7f0000" channel="red" orientation="vertical" height="size-3600" />
  <ColorSlider defaultValue="#7f0000" channel="red" width="size-3600" maxWidth="100%" />
</Flex>

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a ColorSlider.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<ColorSlider
  label="Accent Color"
  channel="hue"
  defaultValue="hsl(120, 100%, 50%)"
  contextualHelp={
    <ContextualHelp>
      <Heading>What is an accent color?</Heading>
      <Content>An accent color is the primary foreground color for your theme, used across all components.</Content>
    </ContextualHelp>
  } />

Testing

The ColorSlider features a draggable handle that the user can interact with to change its color value.
Please see the following section in the testing docs for more information on how to simulate this action in your
test suite.

Simulating move event

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Color ColorSwatch (`packages/@adobe/react-spectrum/docs/color/ColorSwatch.mdx`)

{/* Copyright 2024 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 colorTypes from 'docs:@react-stately/color';
import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorSwatch} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

category: Color keywords: [color swatch]

ColorSwatch

{docs.exports.ColorSwatch.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorSwatch']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/swatch/'}
]}
since="3.35.0" />

Example

tsx example
<ColorSwatch color="#f00" />

Value

ColorSwatch accepts a value via the color prop. The value should be a color string or object.

In the example below, the function is used to parse the initial color from an HSL string. This is passed to the value prop of a ColorSlider, and the color prop of a ColorSwatch to show a preview of the selected color.

tsx example
import {parseColor, ColorSlider} from '@react-spectrum/color';

function Example() {
  let [color, setColor] = React.useState(parseColor('hsl(0, 100%, 50%)'));
  return (
    <Flex direction="column" gap="size-100">
      <ColorSlider value={color} onChange={setColor} channel="hue" />
      <ColorSwatch color={color} />
    </Flex>
  );
}

Fully transparent colors are displayed with a red slash through the swatch, while partially transparent colors are shown with a checkerboard background.

tsx example
import {parseColor, ColorSlider} from '@react-spectrum/color';

function Example() {
  let [color, setColor] = React.useState(parseColor('hsla(0, 100%, 50%, 0)'));
  return (
    <Flex direction="column" gap="size-100">
      <ColorSlider value={color} onChange={setColor} channel="alpha" />
      <ColorSwatch color={color} />
    </Flex>
  );
}

Labeling

By default, ColorSwatch includes a localized color description for screen reader users (e.g. "dark vibrant blue") as an aria-label. If you have a more specific color name (e.g. Pantone colors), the automatically generated color description can be overridden via the colorName prop. An additional label describing the context of the color swatch (e.g. "Background color") can also be provided via the aria-label or aria-labelledby props.

In the example below, the full accessible name of the color swatch will be "Fire truck red, Background color".

tsx example
<ColorSwatch color="#f00" aria-label="Background color" colorName="Fire truck red" />

Props

Visual options

Size

View guidelines

tsx example
<Flex gap="size-100">
  <ColorSwatch color="#ff0" size="XS" />
  <ColorSwatch color="#ff0" size="S" />
  <ColorSwatch color="#ff0" size="M" />
  <ColorSwatch color="#ff0" size="L" />
</Flex>

Rounding

View guidelines

tsx example
<Flex gap="size-100">
  <ColorSwatch color="#0ff" rounding="none" />
  <ColorSwatch color="#0ff" rounding="default" />
  <ColorSwatch color="#0ff" rounding="full" />
</Flex>

Custom width

View guidelines

tsx example
<ColorSwatch color="#00f" width="size-1000" />

Packages @Adobe React Spectrum Docs Color ColorSwatchPicker (`packages/@adobe/react-spectrum/docs/color/ColorSwatchPicker.mdx`)

{/* Copyright 2024 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 colorTypes from 'docs:@react-stately/color';
import docs from 'docs:@react-spectrum/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorSwatchPicker, ColorSwatch} from '@react-spectrum/color';

category: Color keywords: [color swatch]

ColorSwatchPicker

{docs.exports.ColorSwatchPicker.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorSwatchPicker']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/swatch-group/'}
]}
since="3.35.0" />

Example

tsx example
<ColorSwatchPicker>
  <ColorSwatch color="#A00" />
  <ColorSwatch color="#f80" />
  <ColorSwatch color="#080" />
  <ColorSwatch color="#08f" />
  <ColorSwatch color="#088" />
  <ColorSwatch color="#008" />
</ColorSwatchPicker>

Value

ColorSwatchPicker accepts either an uncontrolled default value or a controlled value, provided using the defaultValue or value props respectively. The value provided to either of these props should be a color string or object. The value is matched against the color of each ColorSwatch, including equivalent colors in different color spaces.

In the example below, the function is used to parse the initial color from a HSL string so that value's type remains consistent.

tsx example
import {parseColor} from '@react-spectrum/color';

function Example() {
  let [color, setColor] = React.useState(parseColor('hsl(0, 100%, 33.33%)'));

  return (
    <ColorSwatchPicker value={color} onChange={setColor}>
      <ColorSwatch color="#A00" />
      <ColorSwatch color="#f80" />
      <ColorSwatch color="#080" />
    </ColorSwatchPicker>
  );
}

Note: ColorSwatches rendered inside a ColorSwatchPicker must have unique colors, even between different color spaces. For example, the values #f00, hsl(0, 100%, 50%), and hsb(0, 100%, 100%) are all equivalent and considered duplicates. This is required so that selection behavior works properly.

Labeling

By default, ColorSwatchPicker has an aria-label with the localized string "Color swatches". This can be overridden with a more specific accessibility label using the aria-label or aria-labelledby props. All labels should be localized.

tsx example
<ColorSwatchPicker aria-label="Fill color">
  <ColorSwatch color="#A00" />
  <ColorSwatch color="#f80" />
  <ColorSwatch color="#080" />
</ColorSwatchPicker>

Events

ColorSwatchPicker accepts an onChange prop which is triggered whenever the value is edited by the user. It receives a object as a parameter.

The example below uses onChange to update a separate element with the color value as a string.

tsx example
import {parseColor} from '@react-spectrum/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('#A00'));

  return (
    <div>
      <ColorSwatchPicker value={value} onChange={setValue}>
        <ColorSwatch color="#A00" />
        <ColorSwatch color="#f80" />
        <ColorSwatch color="#080" />
        <ColorSwatch color="#08f" />
        <ColorSwatch color="#088" />
        <ColorSwatch color="#008" />
      </ColorSwatchPicker>
      <p>Selected color: {value.toString('rgb')}</p>
    </div>
  );
}

Props

Visual options

Size

View guidelines

tsx example
<ColorSwatchPicker size="XS">
  <ColorSwatch color="#A00" />
  <ColorSwatch color="#f80" />
  <ColorSwatch color="#080" />
  <ColorSwatch color="#08f" />
</ColorSwatchPicker>

Density

View guidelines

tsx example
<ColorSwatchPicker density="compact">
  <ColorSwatch color="#A00" />
  <ColorSwatch color="#f80" />
  <ColorSwatch color="#080" />
  <ColorSwatch color="#08f" />
</ColorSwatchPicker>

Rounding

View guidelines

Only use rounded corners if the ColorSwatchPicker is displayed on a single row.

tsx example
<ColorSwatchPicker rounding="full">
  <ColorSwatch color="#A00" />
  <ColorSwatch color="#f80" />
  <ColorSwatch color="#080" />
  <ColorSwatch color="#08f" />
</ColorSwatchPicker>

Packages @Adobe React Spectrum Docs Color ColorWheel (`packages/@adobe/react-spectrum/docs/color/ColorWheel.mdx`)

{/* Copyright 2020 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 docs from 'docs:@react-spectrum/color';
import colorTypes from 'docs:@react-stately/color';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Layout} from '@react-spectrum/docs';
import packageData from '@react-spectrum/color/package.json';
import statelyDocs from 'docs:@react-stately/color';

export default Layout;

jsx import
import {ColorWheel} from '@react-spectrum/color';
import {Flex} from '@react-spectrum/layout';

category: Color keywords: [color wheel, slider]

ColorWheel

{docs.exports.ColorWheel.description}

<HeaderInfo
packageData={packageData}
componentNames={['ColorWheel']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/color-wheel/'}
]}
since="3.35.0" />

Example

tsx example
<ColorWheel defaultValue="hsl(30, 100%, 50%)" />

Value

A ColorWheel's value specifies the position of the ColorWheel's thumb on the track, and accepts a string or object.
It is initialized as hsl(0, 100%, 50%) by default, but an alternative initial uncontrolled value can be provided using the defaultValue prop.
Alternatively, a controlled value can be provided using the value prop. Note that only HSB(A) values are supported as valid values.

In the example below, the function is used to parse the initial color from a HSL string
so that value's type remains consistent.

tsx example
import {parseColor} from '@react-stately/color';

function Example() {
  let [value, setValue] = React.useState(parseColor('hsl(30, 100%, 50%)'));
  return (
    <Flex gap="size-300" wrap>
      <Flex direction="column" alignItems="center">
        <label id="label-1">Hue (uncontrolled)</label>
        <ColorWheel
          defaultValue="hsl(30, 100%, 50%)"
          aria-labelledby="label-1" />
      </Flex>
      <Flex direction="column" alignItems="center">
        <label id="label-2">Hue (controlled)</label>
        <ColorWheel
          value={value}
          onChange={setValue}
          aria-labelledby="label-1" />
      </Flex>
    </Flex>
  );
}

HTML forms

ColorWheel supports the name prop for integration with HTML forms. The value will be submitted as a number between 0 and 360 degrees.

tsx example
<ColorWheel name="hue" />

Labeling

By default, a localized string for the "hue" channel name is used as the aria-label for the ColorWheel. When a custom aria-label or aria-labelledby
is provided, it should be localized accordingly.

Events

ColorWheel supports two events: onChange and onChangeEnd. onChange is triggered whenever the ColorWheel's handle is dragged, and onChangeEnd
is triggered when the user stops dragging the handle. Both events receive a object
as a parameter.

The example below uses onChange and onChangeEnd to update two separate elements with the ColorWheel's value.

tsx example
function Example() {
  let [currentValue, setCurrentValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));
  let [finalValue, setFinalValue] = React.useState(parseColor('hsl(50, 100%, 50%)'));

  return (
    <div>
      <ColorWheel
        value={currentValue}
        onChange={setCurrentValue}
        onChangeEnd={setFinalValue}
      />
      <pre>Current value: {currentValue.toString('hsl')}</pre>
      <pre>Final value: {finalValue.toString('hsl')}</pre>
    </div>
  );
}

Props

Visual options

Disabled

View guidelines

tsx example
<ColorWheel isDisabled />

Size

View guidelines

tsx example
<ColorWheel size="size-1600" />

Testing

The ColorWheel features a draggable handle that the user can interact with to change its color value.
Please see the following section in the testing docs for more information on how to simulate this action in your
test suite.

Simulating move event

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Combobox ComboBox (`packages/@adobe/react-spectrum/docs/combobox/ComboBox.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/combobox';
import comboboxUtils from 'docs:@react-aria/test-utils';
import packageData from '@react-spectrum/combobox/package.json';
import {HeaderInfo, PropTable, PageDescription, ClassAPI, VersionBadge} from '@react-spectrum/docs';

jsx import
import Add from '@spectrum-icons/workflow/Add';
import Alert from '@spectrum-icons/workflow/Alert';
import Bell from '@spectrum-icons/workflow/Bell';
import {ComboBox, Item, Section} from '@react-spectrum/combobox';
import Draw from '@spectrum-icons/workflow/Draw';
import {Flex} from '@react-spectrum/layout';
import {Text} from '@react-spectrum/text';
import {useFilter} from '@react-aria/i18n';
import {Avatar} from "@react-spectrum/avatar";

category: Pickers keywords: [collections, dropdown]

ComboBox

{docs.exports.ComboBox.description}

<HeaderInfo
packageData={packageData}
componentNames={['ComboBox', 'Item', 'Section']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/combo-box/'}
]}
since="3.12.0" />

Example

tsx example
<ComboBox label="Favorite Animal">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Content

ComboBox follows the Collection Components API, accepting both static and dynamic collections.
Similar to Picker, ComboBox accepts `` elements as children, each with a key prop. Basic usage of ComboBox, seen in the example above, shows multiple options populated with a string.
Static collections, as in this example, can be used when the full list of options is known ahead of time.

Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time.
Providing the data in this way allows ComboBox to automatically cache the rendering of each item, which dramatically improves performance.

As seen below, an iterable list of options is passed to the ComboBox using the defaultItems prop. Each item accepts a key prop, which is passed to the onSelectionChange handler to identify the selected item.
Alternatively, if the item objects contain an id property, as shown in the example below, then this is used automatically and a key prop is not required. See the Events section for more detail on selection.

tsx example
function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];
  let [majorId, setMajorId] = React.useState(null);

  return (
    <>
      <p>Topic id: {majorId}</p>
      <ComboBox
        label="Pick an engineering major"
        defaultItems={options}
        onSelectionChange={setMajorId}>
        {item => <Item>{item.name}</Item>}
      </ComboBox>
    </>
  );
}

Alternatively, passing your list of options to ComboBox's items prop will cause the list of items to be controlled, useful for when you want to provide your own
filtering logic. See the Custom Filtering section for more detail.

Trays

On mobile, ComboBox automatically displays in a tray instead of a popover to improve usability. The tray contains a search field that mobile users
can use to filter the options available via text.

Internationalization

To internationalize a ComboBox, a localized string should be passed to the children of each Item.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ComboBox is automatically flipped.

Value

A ComboBox's value is empty by default, but an initial, uncontrolled, value can be provided using the defaultInputValue prop.
Alternatively, a controlled value can be provided using the inputValue prop. Note that the input value of the ComboBox does not affect
the ComboBox's selected option. See Events for more details on input change events.

tsx example
function Example() {
  let options = [
    {id: 1, name: 'Adobe Photoshop'},
    {id: 2, name: 'Adobe XD'},
    {id: 3, name: 'Adobe InDesign'},
    {id: 4, name: 'Adobe AfterEffects'},
    {id: 5, name: 'Adobe Illustrator'},
    {id: 6, name: 'Adobe Lightroom'},
    {id: 7, name: 'Adobe Premiere Pro'},
    {id: 8, name: 'Adobe Fresco'},
    {id: 9, name: 'Adobe Dreamweaver'}
  ];
  let [value, setValue] = React.useState('Adobe XD');

  return (
    <Flex gap="size-150" wrap>
      <ComboBox
        label="Adobe product (Uncontrolled)"
        defaultItems={options}
        defaultInputValue="Adobe XD">
        {item => <Item>{item.name}</Item>}
      </ComboBox>

      <ComboBox
        label="Pick an Adobe product (Controlled)"
        defaultItems={options}
        inputValue={value}
        onInputChange={setValue}>
        {item => <Item>{item.name}</Item>}
      </ComboBox>
    </Flex>
  );
}

Custom Value

By default on blur, a ComboBox will either reset its input value to match the selected option's text or clear its input value if an option has not been selected yet.
If you would like to allow the end user to provide a custom input value to the ComboBox, the allowsCustomValue prop can be used to override the above behavior.

tsx example
function Example() {
  let options = [
    {name: 'Apple'},
    {name: 'Banana'},
    {name: 'Orange'},
    {name: 'Honeydew'},
    {name: 'Grapes'},
    {name: 'Watermelon'},
    {name: 'Cantaloupe'},
    {name: 'Pear'}
  ];

  return (
    <>
      <p>
        Please indicate what fruit you would like included with your delivery. If your desired choice does not appear in the list
        feel free to write your own selection.
      </p>
      <ComboBox
        label="Preferred fruit"
        defaultItems={options}
        allowsCustomValue>
        {item => <Item key={item.name}>{item.name}</Item>}
      </ComboBox>
    </>
  );
}

HTML forms

ComboBox supports the name prop for integration with HTML forms. By default, the text in the input field will be submitted to the server. If the formValue prop is set to "key", the key of the selected item will be submitted instead.

tsx example
<Flex gap="size-200" wrap>
  <ComboBox
    label="Ice cream flavor"
    ///- begin highlight -///
    name="iceCream"
    allowsCustomValue
    ///- end highlight -///
  >
    <Item>Chocolate</Item>
    <Item>Mint</Item>
    <Item>Strawberry</Item>
    <Item>Vanilla</Item>
  </ComboBox>
  <ComboBox
    label="Favorite Animal"
    ///- begin highlight -///
    name="favoriteAnimalId"
    formValue="key"
    ///- end highlight -///
  >
    <Item key="panda">Panda</Item>
    <Item key="cat">Cat</Item>
    <Item key="dog">Dog</Item>
  </ComboBox>
</Flex>

Labeling

ComboBox can be labeled using the label prop. If the ComboBox is a required field, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<ComboBox label="Favorite Animal" isRequired necessityIndicator="icon">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>
tsx example
<ComboBox label="Favorite Animal" isRequired necessityIndicator="label">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>
tsx example
<ComboBox label="Favorite Animal" necessityIndicator="label">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the ComboBox for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a ComboBox, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Selection

Setting a selected option can be done by using the defaultSelectedKey or selectedKey prop. The selected key corresponds to the key of an item. See Events for more details on selection events.
Additionally, see the react-stately Selection docs for caveats regarding selection prop typing.

tsx example
import type {Key} from '@adobe/react-spectrum';

function Example() {
  let options = [
    {id: 1, name: 'Adobe Photoshop'},
    {id: 2, name: 'Adobe XD'},
    {id: 3, name: 'Adobe InDesign'},
    {id: 4, name: 'Adobe AfterEffects'},
    {id: 5, name: 'Adobe Illustrator'},
    {id: 6, name: 'Adobe Lightroom'},
    {id: 7, name: 'Adobe Premiere Pro'},
    {id: 8, name: 'Adobe Fresco'},
    {id: 9, name: 'Adobe Dreamweaver'}
  ];
  let [productId, setProductId] = React.useState<Key>(9);

  return (
    <Flex gap="size-150" wrap>
      <ComboBox
        label="Pick an Adobe product (uncontrolled)"
        defaultItems={options}
        defaultSelectedKey={9}>
        {item => <Item>{item.name}</Item>}
      </ComboBox>

      <ComboBox
        label="Pick an Adobe product (controlled)"
        defaultItems={options}
        selectedKey={productId}
        onSelectionChange={selected => setProductId(selected)}>
        {item => <Item>{item.name}</Item>}
      </ComboBox>
    </Flex>
  );
}

By default, interacting with an item in a ComboBox selects it and updates the input value. Alternatively, items may be links to another page or website. This can be achieved by passing the href prop to the `` component. Interacting with link items navigates to the provided URL and does not update the selection or input value.

tsx example
<ComboBox label="Tech company websites">
  <Item href="https://adobe.com/" target="_blank">Adobe</Item>
  <Item href="https://apple.com/" target="_blank">Apple</Item>
  <Item href="https://google.com/" target="_blank">Google</Item>
  <Item href="https://microsoft.com/" target="_blank">Microsoft</Item>
</ComboBox>

Client side routing

The `` component works with frameworks and client side routers like Next.js and React Router. As with other React Spectrum components that support links, this works via the Provider component at the root of your app. See the client side routing guide to learn how to set this up.

Sections

ComboBox supports sections in order to group options. Sections can be used by wrapping groups of items in a Section element. Each Section takes a title and key prop.

Static items

tsx example
<ComboBox label="Preferred fruit or vegetable">
  <Section title="Fruit">
    <Item key="Apple">Apple</Item>
    <Item key="Banana">Banana</Item>
    <Item key="Orange">Orange</Item>
    <Item key="Honeydew">Honeydew</Item>
    <Item key="Grapes">Grapes</Item>
    <Item key="Watermelon">Watermelon</Item>
    <Item key="Cantaloupe">Cantaloupe</Item>
    <Item key="Pear">Pear</Item>
  </Section>
  <Section title="Vegetable">
    <Item key="Cabbage">Cabbage</Item>
    <Item key="Broccoli">Broccoli</Item>
    <Item key="Carrots">Carrots</Item>
    <Item key="Lettuce">Lettuce</Item>
    <Item key="Spinach">Spinach</Item>
    <Item key="Bok Choy">Bok Choy</Item>
    <Item key="Cauliflower">Cauliflower</Item>
    <Item key="Potatoes">Potatoes</Item>
  </Section>
</ComboBox>

Dynamic items

Sections used with dynamic items are populated from a hierarchical data structure. Please note that Section takes an array of data using the items prop only.

tsx example
function Example() {
  let options = [
    {name: 'Fruit', children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Orange'},
      {name: 'Honeydew'},
      {name: 'Grapes'},
      {name: 'Watermelon'},
      {name: 'Cantaloupe'},
      {name: 'Pear'}
    ]},
    {name: 'Vegetable', children: [
      {name: 'Cabbage'},
      {name: 'Broccoli'},
      {name: 'Carrots'},
      {name: 'Lettuce'},
      {name: 'Spinach'},
      {name: 'Bok Choy'},
      {name: 'Cauliflower'},
      {name: 'Potatoes'}
    ]}
  ];

  return (
    <ComboBox label="Preferred fruit or vegetable" defaultItems={options}>
      {item => (
        <Section key={item.name} items={item.children} title={item.name}>
          {item => <Item key={item.name}>{item.name}</Item>}
        </Section>
      )}
    </ComboBox>
  );
}

Events

ComboBox supports selection via mouse, keyboard, and touch. You can handle all of these via the onSelectionChange
prop. ComboBox will pass the selected key to the onSelectionChange handler.
Additionally, ComboBox accepts an onInputChange prop which is triggered whenever the value is edited by the user, whether through typing or option
selection.

The example below uses onSelectionChange and onInputChange to update the selection and input value stored in React state.

tsx example
function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];

  let [value, setValue] = React.useState('');
  let [majorId, setMajorId] = React.useState('');

  let onSelectionChange = (id) => {
    setMajorId(id);
  };

  let onInputChange = (value) => {
    setValue(value)
  };

  return (
    <>
      <p>Current selected major id: {majorId}</p>
      <p>Current input text: {value}</p>
      <ComboBox
        label="Pick a engineering major"
        defaultItems={options}
        selectedKey={majorId}
        onSelectionChange={onSelectionChange}
        onInputChange={onInputChange}>
        {item => <Item>{item.name}</Item>}
      </ComboBox>
    </>
  );
}

Fully controlled ComboBox

When a ComboBox has multiple controlled properties (e.g.inputValue, selectedKey, items), it is important to note that an update to one of these properties will
not automatically update the others. Each interaction done in the ComboBox will only trigger its associated event handler. For example, typing in the field will only
trigger onInputChange whereas selecting an item from the ComboBox menu will only trigger onSelectionChange so it is your responsibility to update the other
controlled properties accordingly. Note that you should provide an onSelectionChange handler for a ComboBox with controlled input value and open state. This way, you can properly
control the menu's open state when the user selects an option or blurs from the field.

The below example demonstrates how you would construct the same example above in a completely controlled fashion.

tsx example
import {useTreeData} from '@react-stately/data';

function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];

  let [fieldState, setFieldState] = React.useState({
    selectedKey: '',
    inputValue: ''
  });

  let list = useTreeData({
    initialItems: options
  });

  let onSelectionChange = (key) => {
    setFieldState({
      inputValue: list.getItem(key)?.value.name ?? '',
      selectedKey: key
    });
  };

  let onInputChange = (value) => {
    setFieldState(prevState => ({
      inputValue: value,
      selectedKey: value === '' ? null : prevState.selectedKey
    }));
  };

  return (
     <>
      <p>Current selected major id: {fieldState.selectedKey}</p>
      <p>Current input text: {fieldState.inputValue}</p>
       <ComboBox
        label="Pick a engineering major"
        defaultItems={list.items}
        selectedKey={fieldState.selectedKey}
        inputValue={fieldState.inputValue}
        onSelectionChange={onSelectionChange}
        onInputChange={onInputChange}>
        {item => <Item>{item.value.name}</Item>}
      </ComboBox>
     </>
  );
}

Complex items

Items within ComboBox also allow for additional content used to better communicate options. Icons, avatars, and descriptions can be added to the children of Item as shown in the example below.
If a description is added, the prop slot="description" must be used to distinguish the different <Text> elements.
See Icon's labeling section and Avatar's accessibility section for more information on how to label these elements.

tsx example
<ComboBox label="Select action">
  <Item textValue="Add to queue">
    <Add />
    <Text>Add to queue</Text>
    <Text slot="description">Add to current watch queue.</Text>
  </Item>
  <Item textValue="Add review">
    <Draw />
    <Text>Add review</Text>
    <Text slot="description">Post a review for the episode.</Text>
  </Item>
    <Item textValue="Subscribe to series">
    <Bell />
    <Text>Subscribe to series</Text>
    <Text slot="description">Add series to your subscription list and be notified when a new episode airs.</Text>
  </Item>
  <Item textValue="Report">
    <Alert />
    <Text>Report</Text>
    <Text slot="description">Report an issue/violation.</Text>
  </Item>
</ComboBox>

With avatars

tsx example
<ComboBox label="Select a user">
  <Item textValue="User 1">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 1</Text>
  </Item>
  <Item textValue="User 2">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 2</Text>
  </Item>
  <Item textValue="User 3">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 3</Text>
  </Item>
  <Item textValue="User 4">
    <Avatar src="https://i.imgur.com/kJOwAdv.png" />
    <Text>User 4</Text>
  </Item>
</ComboBox>

Asynchronous loading

ComboBox supports loading data asynchronously, and will display a progress circle reflecting the current load state,
set by the loadingState prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the onLoadMore prop.

This example uses the useAsyncList hook to handle loading the data. See the docs for more information.

tsx example
import {useAsyncList} from '@react-stately/data';

interface Character {
  name: string
}

function AsyncLoadingExample() {
  let list = useAsyncList<Character>({
    async load({signal, cursor, filterText}) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      // If no cursor is available, then we're loading the first page,
      // filtering the results returned via a query string that
      // mirrors the ComboBox input text.
      // Otherwise, the cursor is the next URL to load,
      // as returned from the previous page.
      let res = await fetch(cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`, {signal});
      let json = await res.json();

      return {
        items: json.results,
        cursor: json.next
      };
    }
  });

  return (
    <ComboBox
      label="Star Wars Character Lookup"
      items={list.items}
      inputValue={list.filterText}
      onInputChange={list.setFilterText}
      loadingState={list.loadingState}
      onLoadMore={list.loadMore}>
      {item => <Item key={item.name}>{item.name}</Item>}
    </ComboBox>
  );
}

When both inputValue and selectedKey are controlled, and the selectedKey is set to an initial value before the items load, you must update the inputValue when the items load.
This can be done by returning filterText from the useAsyncList load function.

The example below demonstrates how you could update the input value for your selected key once your list of items has been fetched.

text
/* Detailed source-code truncated for AI context efficiency. */

Validation

ComboBox supports the isRequired prop to ensure the user enters a value, as well as custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the combo box or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <ComboBox label="Favorite animal" name="animal" isRequired>
  {/*- end highlight -*/}
    <Item>Aardvark</Item>
    <Item>Cat</Item>
    <Item>Dog</Item>
    <Item>Kangaroo</Item>
    <Item>Panda</Item>
    <Item>Snake</Item>
  </ComboBox>
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, ComboBox displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Custom Filtering

By default, ComboBox uses a string "contains" filtering strategy when deciding what items to display in the dropdown menu. This filtering strategy can be overwritten
by filtering the list of items yourself and passing the filtered list to the ComboBox via the items prop.

The example below uses a string "startsWith" filter function obtained from the useFilter hook to display items that start with the ComboBox's current input
value only. By using the menuTrigger returned by onOpenChange, it also handles displaying the entire option list regardless of the current filter value when the ComboBox menu is
opened via the trigger button or arrow keys. menuTrigger tells you if the menu was opened manually by the user ("manual"), by focusing the ComboBox ("focus"), or by
changes in the input field ("input"), allowing you to make updates to other controlled aspects of your ComboBox accordingly.

tsx example
function Example() {
  let options = [
    {id: 1, email: '[email protected]'},
    {id: 2, email: '[email protected]'},
    {id: 3, email: '[email protected]'},
    {id: 4, email: '[email protected]'},
    {id: 5, email: '[email protected]'},
    {id: 6, email: '[email protected]'},
    {id: 7, email: '[email protected]'},
    {id: 8, email: '[email protected]'},
    {id: 9, email: '[email protected]'}
  ];

  let [showAll, setShowAll] = React.useState(false);
  let [filterValue, setFilterValue] = React.useState('');
  let {startsWith} = useFilter({sensitivity: 'base'});
  let filteredItems = React.useMemo(() => options.filter(item => startsWith(item.email, filterValue)), [options, filterValue]);

  return (
    <ComboBox
      onOpenChange={(isOpen, menuTrigger) => {
        // Show all items if menu is opened manually
        // i.e. by the arrow keys or trigger button
        if (menuTrigger === 'manual' && isOpen) {
          setShowAll(true);
        }
      }}
      width="size-3000"
      label="To:"
      items={showAll ? options : filteredItems}
      inputValue={filterValue}
      onInputChange={(value) => {
        setShowAll(false);
        setFilterValue(value);
      }}
      allowsCustomValue>
      {item => <Item>{item.email}</Item>}
    </ComboBox>
  );
}

Trigger options

By default, the ComboBox's menu is opened when the user types into the input field ("input"). There are two other supported modes: one where the menu opens when the ComboBox is focused ("focus") and
the other where the menu only opens when the user clicks or taps on the ComboBox's field button ("manual"). These can be set by providing "focus" or "manual" to the menuTrigger prop.
Guidelines on when to use a specific mode can be found here. Note that the mobile ComboBox experience requires the end user to press the ComboBox button
to open the tray regardless of the menuTrigger setting.

tsx example
<ComboBox label="Select action" menuTrigger="focus">
  <Item textValue="Add to queue">
    <Add />
    <Text>Add to queue</Text>
    <Text slot="description">Add to current watch queue.</Text>
  </Item>
  <Item textValue="Add review">
    <Draw />
    <Text>Add review</Text>
    <Text slot="description">Post a review for the episode.</Text>
  </Item>
    <Item textValue="Subscribe to series">
    <Bell />
    <Text>Subscribe to series</Text>
    <Text slot="description">Add series to your subscription list and be notified when a new episode airs.</Text>
  </Item>
  <Item textValue="Report">
    <Alert />
    <Text>Report</Text>
    <Text slot="description">Report an issue/violation.</Text>
  </Item>
</ComboBox>
tsx example
<ComboBox label="Select action" menuTrigger="manual">
  <Item textValue="Add to queue">
    <Add />
    <Text>Add to queue</Text>
    <Text slot="description">Add to current watch queue.</Text>
  </Item>
  <Item textValue="Add review">
    <Draw />
    <Text>Add review</Text>
    <Text slot="description">Post a review for the episode.</Text>
  </Item>
    <Item textValue="Subscribe to series">
    <Bell />
    <Text>Subscribe to series</Text>
    <Text slot="description">Add series to your subscription list and be notified when a new episode airs.</Text>
  </Item>
  <Item textValue="Report">
    <Alert />
    <Text>Report</Text>
    <Text slot="description">Report an issue/violation.</Text>
  </Item>
</ComboBox>

Props

Visual options

Label alignment and position

View guidelines

By default, the label is positioned above the ComboBox. The labelPosition prop can be used to position the label to the side.
The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the ComboBox and "end" refers to the right most edge.
For right-to-left (RTL) languages, this is flipped.

tsx example
<ComboBox label="Favorite Animal" labelPosition="side" labelAlign="end">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Quiet

View guidelines

tsx example
<ComboBox label="Favorite Animal" isQuiet>
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Disabled

View guidelines

tsx example
<ComboBox label="Favorite Animal" isDisabled>
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Read only

View guidelines

tsx example
<ComboBox label="Favorite Animal" isReadOnly selectedKey="red panda">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Help text

View guidelines

Both a description and an error message can be supplied to a ComboBox. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
function Example() {
    let [animalId, setAnimalId] = React.useState(null);
    let options = [
      {id: 1, name: 'Aardvark'},
      {id: 2, name: 'Cat'},
      {id: 3, name: 'Dog'},
      {id: 4, name: 'Kangaroo'},
      {id: 5, name: 'Koala'},
      {id: 6, name: 'Penguin'},
      {id: 7, name: 'Snake'},
      {id: 8, name: 'Turtle'},
      {id: 9, name: 'Wombat'}
    ];
  let isValid = React.useMemo(() => animalId !== 2 && animalId !== 7, [animalId]);

  return (
    <ComboBox
      validationState={!animalId ? undefined : isValid ? 'valid' : 'invalid'}
      label="Favorite animal"
      description="Pick your favorite animal, you will be judged."
      errorMessage={animalId === 2 ? 'The author of this example is a dog person.' : 'Oh no it\'s a snake! Choose anything else.'}
      items={options}
      selectedKey={animalId}
      onSelectionChange={selected => setAnimalId(selected)}>
      {item => <Item>{item.name}</Item>}
    </ComboBox>
  );
}

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a ComboBox.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<ComboBox
  label="Engineering major"
  contextualHelp={
    <ContextualHelp variant="info">
      <Heading>Major changes</Heading>
      <Content>Once you have changed your major, you cannot change it back.</Content>
    </ContextualHelp>
  }>
  <Item>Aerospace</Item>
  <Item>Mechanical</Item>
  <Item>Civil</Item>
  <Item>Nuclear</Item>
  <Item>Industrial</Item>
  <Item>Chemical</Item>
  <Item>Agricultural</Item>
  <Item>Electrical</Item>
</ComboBox>

Custom widths

View guidelines

tsx example
<ComboBox label="Favorite Animal" width="size-6000" maxWidth="100%">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>
tsx example
<ComboBox label="Favorite Animal" direction="top">
  <Item key="red panda">Red Panda</Item>
  <Item key="cat">Cat</Item>
  <Item key="dog">Dog</Item>
  <Item key="aardvark">Aardvark</Item>
  <Item key="kangaroo">Kangaroo</Item>
  <Item key="snake">Snake</Item>
</ComboBox>

Testing

The ComboBox features an overlay that transitions in and out of the page as it is opened and closed. Depending on
your device configuration, this overlay may render as a tray or a dropdown and the trigger itself may render as a button or a input. Additionally,
the ComboBox features automatic virtualization and may need specific mocks in a test environment to enable said virtualization properly.
Please see the following sections in the testing docs for more information on how to handle these
behaviors in your test suite.

Timers

Desktop vs Mobile

Virtualized Components

Long press

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.

Test utils

@react-spectrum/test-utils offers common combobox interaction utilities which you may find helpful when writing tests. See here for more information on how to setup these utilities
in your tests. Below is the full definition of the combobox tester and a sample of how you could use it in your test suite.

ts
// Combobox.test.ts
import {render, within} from '@testing-library/react';
import {theme} from '@react-spectrum/theme-default';
import {User} from '@react-spectrum/test-utils';

let testUtilUser = new User({interactionType: 'mouse'});
// Other setup, be sure to check out the suggested mocks mentioned above in https://react-spectrum.adobe.com/react-spectrum/ComboBox.html#testing

it('ComboBox can select an option via keyboard', async function () {
  // Render your test component/app and initialize the combobox tester
  let {getByTestId} = render(
    <Provider theme={defaultTheme}>
      <ComboBox data-testid="test-combobox">
        ...
      </ComboBox>
    </Provider>
  );
  let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'});

  await comboboxTester.open();
  expect(comboboxTester.getListbox()).toBeInTheDocument();

  let options = comboboxTester.getOptions();
  await comboboxTester.toggleOptionSelection({option: options[0]});
  expect(comboboxTester.getCombobox().value).toBe('One');
  expect(comboboxTester.getListbox()).not.toBeInTheDocument();
});

Packages @Adobe React Spectrum Docs Contextualhelp ContextualHelp (`packages/@adobe/react-spectrum/docs/contextualhelp/ContextualHelp.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/contextualhelp';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/contextualhelp/package.json';

jsx import
import {ContextualHelp} from '@react-spectrum/contextualhelp';
import {Content, Footer} from '@react-spectrum/view';
import {Heading, Text} from '@react-spectrum/text';
import {Link} from '@react-spectrum/link';
import {Flex} from '@react-spectrum/layout';

category: Overlays keywords: [popover, field, input]

ContextualHelp

{docs.exports.ContextualHelp.description}

<HeaderInfo
packageData={packageData}
componentNames={['ContextualHelp']}
sourceData={[ {type: 'Spectrum', url: 'https://spectrum.adobe.com/page/contextual-help/'} ]}
since="3.16.0" />

Example

tsx example
<ContextualHelp variant="info">
  <Heading>Need help?</Heading>
  <Content><Text>If you're having issues accessing your account, contact our customer support team for help.</Text></Content>
</ContextualHelp>

Content

Unlike Dialog, the layout in ContextualHelp is very deliberate. Every ContextualHelp component is triggered as a popover where the
trigger element is a quiet action button with either a help or info icon. Much like Dialog, however, ContextualHelp has sections that
can be populated by providing the following components to your ContextualHelp as children:
Heading (title), Content (body), and Footer (link).
Each of these components are required in a Spectrum compliant ContextualHelp except for Footer since including a link is optional.

tsx example
<ContextualHelp variant="help">
  <Heading>What is a segment?</Heading>
  <Content><Text>Segments identify who your visitors are, what devices and services they use, where they navigated from, and much more.</Text></Content>
  <Footer><Link>Learn more about segments</Link></Footer>
</ContextualHelp>

Placement

ContextualHelp supports Dialog placement options for when the positioning of the popover needs to customized.

tsx example
<ContextualHelp variant="info" placement="top start">
  <Heading>Placement</Heading>
  <Content><Text>The placement of this contextual help popover has been customized to use top start.</Text></Content>
</ContextualHelp>

Events

ContextualHelp accepts an onOpenChange prop, triggered when the ContextualHelp's popover opens or closes.

The example below uses onOpenChange to update a separate element with the current open state of the Dialog.

tsx example
function Example() {
  let [state, setState] = React.useState(false);

  return (
    <Flex alignItems="center" gap="size-100">
      <ContextualHelp variant="info" onOpenChange={(isOpen) => setState(isOpen)}>
        <Heading>Permission required</Heading>
        <Content><Text>Your admin must grant you permission before you can create a segment.</Text></Content>
      </ContextualHelp>
      <Text>Current open state: {state.toString()}</Text>
    </Flex>
  );
}

Props

Visual options

Info

Use the info icon for brief, specific, contextual guidance. This is best for supplemental or nice-to-know information,
in-line with a label or a component (if there is no label). The content for an info icon’s popover should be instructive in tone.

tsx example
<ContextualHelp variant="info">
  <Heading>Permission required</Heading>
  <Content><Text>Your admin must grant you permission before you can create a segment.</Text></Content>
</ContextualHelp>

Help

Use the help icon for content that offers more detailed, in-depth guidance about a task, UI element, tool, or keyboard shortcuts.
This may include an image, video, or link and should be helpful in tone.

tsx example
<ContextualHelp variant="help">
  <Heading>What is a segment?</Heading>
  <Content><Text>Segments identify who your visitors are, what devices and services they use, where they navigated from, and much more.</Text></Content>
  <Footer><Link>Learn more about segments</Link></Footer>
</ContextualHelp>

Testing

The ContextualHelpTrigger features an overlay that transitions in and out of the page as it is opened and closed.
Please see the following sections in the testing docs for more information on how to handle these behaviors in your test suite.

Timers

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Datepicker DateField (`packages/@adobe/react-spectrum/docs/datepicker/DateField.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/datepicker';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/datepicker/package.json';

jsx import
import {DateField} from '@react-spectrum/datepicker';
import {Flex} from '@react-spectrum/layout';

category: Date and Time keywords: [input, form, field, date, time]

DateField

{docs.exports.DateField.description}

<HeaderInfo
packageData={packageData}
componentNames={['DateField']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<DateField label="Event date" />

Value

A DateField displays a placeholder by default. An initial, uncontrolled value can be provided to the DateField using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Date values are provided using objects in the @internationalized/date package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. DateField supports values of the following types:

  • – a date without any time components. May be parsed from a string representation using the function. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event.
  • – a date with a time, but not in any specific time zone. May be parsed from a string representation using the function. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.
  • – a date with a time in a specific time zone. May be parsed from a string representation using the , , or functions. Use this type to represent an exact moment in time at a particular location on Earth.
tsx example
import {parseDate} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState(parseDate('2020-02-03'));

  return (
    <Flex gap="size-150" wrap>
      <DateField
        label="Date (uncontrolled)"
        defaultValue={parseDate('2020-02-03')} />
      <DateField
        label="Date (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

Time zones

DateField is time zone aware when a object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

In most cases, your data will come from and be sent to a server as an ISO 8601 formatted string. @internationalized/date includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store.

  • – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. "2021-11-07T00:45[America/Los_Angeles]" or "2021-11-07T00:45-07:00[America/Los_Angeles]"). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.
  • – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. "2021-11-07T07:45:00Z"), or stored with a particular offset (e.g. "2021-11-07T07:45:00-07:00"). A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone.
  • – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for parseAbsolute, and accepts the same formats.
tsx example
import {parseZonedDateTime} from '@internationalized/date';

<DateField
  label="Event date"
  defaultValue={parseZonedDateTime('2022-11-07T00:45[America/Los_Angeles]')} />

DateField displays times in the time zone included in the ZonedDateTime object. The above example is always displayed in Pacific Standard Time because the America/Los_Angeles time zone identifier is provided. @internationalized/date includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below.

tsx example
import {parseAbsoluteToLocal} from '@internationalized/date';

<DateField
  label="Event date"
  defaultValue={parseAbsoluteToLocal('2021-11-07T07:45:00Z')}
/>

Granularity

The granularity prop allows you to control the smallest unit that is displayed by a DateField. By default, CalendarDate values are displayed with "day" granularity (year, month, and day), and CalendarDateTime and ZonedDateTime values are displayed with "minute" granularity. More granular time values can be displayed by setting the granularity prop to "second".

In addition, when a value with a time is provided but you wish to only display the date, you can set the granularity to "day". This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two DateFields are synchronized with the same value, but display different granularities.

tsx example
function Example() {
  let [date, setDate] = React.useState(parseAbsoluteToLocal('2021-04-07T18:45:22Z'));

  return (
    <Flex gap="size-150" wrap>
      <DateField
        label="Date and time"
        granularity="second"
        value={date}
        onChange={setDate} />
      <DateField
        label="Date"
        granularity="day"
        value={date}
        onChange={setDate} />
    </Flex>
  );
}

If no value or defaultValue prop is passed, then the granularity prop also affects which type of value is emitted from the onChange event. Note that by default, time values will not have a time zone because none was supplied. You can override this by setting the placeholderValue prop explicitly. Values emitted from onChange will use the time zone of the placeholder value.

tsx example
import {now} from '@internationalized/date';

<Flex gap="size-150" wrap>
  <DateField
    label="Event date"
    granularity="second" />
  <DateField
    label="Event date"
    placeholderValue={now('America/New_York')}
    granularity="second" />
</Flex>

International calendars

DateField supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the Unicode calendar locale extension, passed to the Provider component.

Selected dates passed to onChange always use the same calendar system as the value or defaultValue prop. If no value or defaultValue is provided, then dates passed to onChange are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.

The below example displays a DateField in the Hindi language, using the Indian calendar. Dates emitted from onChange are in the Gregorian calendar.

tsx example
import {Provider} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(null);
  return (
    <Provider locale="hi-IN-u-ca-indian">
      <DateField label="Date" value={date} onChange={setDate} />
      <p>Selected date: {date?.toString()}</p>
    </Provider>
  );
}

HTML forms

DateField supports the name prop for integration with HTML forms. The value will be submitted to the server as an ISO 8601 formatted string according to the granularity of the value. For example, if the date field allows selecting only a date then a string such as "2023-02-03" will be submitted, and if it allows selecting a time then a string such as "2023-02-03T08:45:00" will be submitted. See the Value section above for more details about the supported value types.

tsx example
<DateField label="Birth date" name="birthday" />

Labeling

A visual label should be provided for the DateField using the label prop. If the DateField is required, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-150" wrap>
  <DateField label="Birth date" />
  <DateField label="Birth date" isRequired necessityIndicator="icon" />
  <DateField label="Birth date" isRequired necessityIndicator="label" />
  <DateField label="Birth date" necessityIndicator="label" />
</Flex>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the DateField for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a DateField, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Events

DateField accepts an onChange prop which is triggered whenever the date is edited by the user. The example below uses onChange to update a separate element with a formatted version of the date in the user's locale and local time zone. This is done by converting the date to a native JavaScript Date object to pass to the formatter.

tsx example
import {getLocalTimeZone} from '@internationalized/date';
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(parseDate('1985-07-03'));
  let formatter = useDateFormatter({dateStyle: 'full'});

  return (
    <>
      <DateField label="Birth date" value={date} onChange={setDate} />
      <p>Selected date: {date ? formatter.format(date.toDate(getLocalTimeZone())): '--'}</p>
    </>
  );
}

Validation

DateField supports the isRequired prop to ensure the user enters a value, as well as minimum and maximum values, and custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the date field or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <DateField label="Appointment date" name="date" isRequired />
  {/*- end highlight -*/}
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, DateField displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Minimum and maximum values

The minValue and maxValue props can also be used to ensure the value is within a specific range. This example only accepts dates after today.

tsx example
import {today} from '@internationalized/date';

<Form validationBehavior="native" maxWidth="size-3000">
  <DateField
    label="Appointment date"
    /*- begin highlight -*/
    minValue={today(getLocalTimeZone())}
    /*- end highlight -*/
    defaultValue={parseDate('2022-02-03')} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Custom validation

The validate function can be used to perform custom validation logic. It receives the current field value, and should return a string or array of strings representing one or more error messages if the value is invalid.

This example validates that the selected date is a weekday and not a weekend according to the current locale.

tsx example
import {isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let {locale} = useLocale();

  return (
    <Form validationBehavior="native" maxWidth="size-3000">
      <DateField
        label="Appointment date"
        /*- begin highlight -*/
        validate={date => date && isWeekend(date, locale) ? 'We are closed on weekends.' : null}
        /*- end highlight -*/
        defaultValue={parseDate('2023-10-28')} />
      <ButtonGroup>
        <Button type="submit" variant="primary">Submit</Button>
        <Button type="reset" variant="secondary">Reset</Button>
      </ButtonGroup>
    </Form>
  );
}

Props

Visual options

Quiet

tsx example
<DateField label="Birth date" isQuiet />

Disabled

tsx example
<DateField label="Birth date" isDisabled />

Read only

The isReadOnly boolean prop makes the DateField's value immutable. Unlike isDisabled, the DateField remains focusable.

tsx example
<DateField label="Birth date" value={today(getLocalTimeZone())} isReadOnly />

Label alignment and position

View guidelines

By default, the label is positioned above the DateField. The labelPosition prop can be used to position the label to the side. The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the DateField and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<DateField label="Birth date" labelPosition="side" labelAlign="end" />

Help text

View guidelines

Both a description and an error message can be supplied to a DateField. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
<Flex gap="size-100" wrap>
  <DateField label="Date" defaultValue={today(getLocalTimeZone())} validationState="valid" description="Select a meeting date." />
  <DateField label="Date" validationState="invalid" errorMessage="Empty input is not allowed." />
</Flex>

DateField also supports displaying the expected date format for the user's locale automatically using the showFormatHelpText prop.

tsx example
<DateField label="Birth date" showFormatHelpText />

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a DateField.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<DateField
  label="Appointment date"
  contextualHelp={
    <ContextualHelp variant="info">
      <Heading>Appointment changes</Heading>
      <Content>Your appointment date cannot be changed once it is scheduled.</Content>
    </ContextualHelp>
  } />

Placeholder value

When no value is set, a placeholder is shown. The format of the placeholder is influenced by the granularity and placeholderValue props. placeholderValue also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys. By default, the placeholderValue is the current date at midnight, but you can set it to a more appropriate value if needed.

tsx example
import {CalendarDate} from '@internationalized/date';

<DateField label="Birth date" placeholderValue={new CalendarDate(1980, 1, 1)} />

Hide time zone

When a ZonedDateTime object is provided as the value of a DateField, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the hideTimeZone option.

tsx example
<DateField
  label="Appointment time"
  defaultValue={parseZonedDateTime('2022-11-07T10:45[America/Los_Angeles]')}
  hideTimeZone />

Hour cycle

By default, DateField displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the hourCycle prop if needed for a specific usecase. This example forces the DateField to use 24-hour time, regardless of the locale.

tsx example
<DateField
  label="Appointment time"
  granularity="minute"
  hourCycle={24} />

Packages @Adobe React Spectrum Docs Datepicker DatePicker (`packages/@adobe/react-spectrum/docs/datepicker/DatePicker.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/datepicker';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/datepicker/package.json';

jsx import
import {DatePicker} from '@react-spectrum/datepicker';
import {Flex} from '@react-spectrum/layout';

category: Date and Time keywords: [input, form, field, date, time]

DatePicker

{docs.exports.DatePicker.description}

<HeaderInfo
packageData={packageData}
componentNames={['DatePicker']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<DatePicker label="Event date" />

Value

A DatePicker displays a placeholder by default. An initial, uncontrolled value can be provided to the DatePicker using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Date values are provided using objects in the @internationalized/date package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. DatePicker supports values of the following types:

  • – a date without any time components. May be parsed from a string representation using the function. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event.
  • – a date with a time, but not in any specific time zone. May be parsed from a string representation using the function. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.
  • – a date with a time in a specific time zone. May be parsed from a string representation using the , , or functions. Use this type to represent an exact moment in time at a particular location on Earth.
tsx example
import {parseDate} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState(parseDate('2020-02-03'));

  return (
    <Flex gap="size-150" wrap>
      <DatePicker
        label="Date (uncontrolled)"
        defaultValue={parseDate('2020-02-03')} />
      <DatePicker
        label="Date (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

Time zones

DatePicker is time zone aware when a object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

In most cases, your data will come from and be sent to a server as an ISO 8601 formatted string. @internationalized/date includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store.

  • – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. "2021-11-07T00:45[America/Los_Angeles]" or "2021-11-07T00:45-07:00[America/Los_Angeles]"). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.
  • – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. "2021-11-07T07:45:00Z"), or stored with a particular offset (e.g. "2021-11-07T07:45:00-07:00"). A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone.
  • – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for parseAbsolute, and accepts the same formats.
tsx example
import {parseZonedDateTime} from '@internationalized/date';

<DatePicker
  label="Event date"
  defaultValue={parseZonedDateTime('2022-11-07T00:45[America/Los_Angeles]')} />

DatePicker displays times in the time zone included in the ZonedDateTime object. The above example is always displayed in Pacific Standard Time because the America/Los_Angeles time zone identifier is provided. @internationalized/date includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below.

tsx example
import {parseAbsoluteToLocal} from '@internationalized/date';

<DatePicker
  label="Event date"
  defaultValue={parseAbsoluteToLocal('2021-11-07T07:45:00Z')}
/>

Granularity

The granularity prop allows you to control the smallest unit that is displayed by a DatePicker. By default, CalendarDate values are displayed with "day" granularity (year, month, and day), and CalendarDateTime and ZonedDateTime values are displayed with "minute" granularity. More granular time values can be displayed by setting the granularity prop to "second".

In addition, when a value with a time is provided but you wish to only display the date, you can set the granularity to "day". This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two DatePickers are synchronized with the same value, but display different granularities.

tsx example
function Example() {
  let [date, setDate] = React.useState(parseAbsoluteToLocal('2021-04-07T18:45:22Z'));

  return (
    <Flex gap="size-150" wrap>
      <DatePicker
        label="Date and time"
        granularity="second"
        value={date}
        onChange={setDate} />
      <DatePicker
        label="Date"
        granularity="day"
        value={date}
        onChange={setDate} />
    </Flex>
  );
}

If no value or defaultValue prop is passed, then the granularity prop also affects which type of value is emitted from the onChange event. Note that by default, time values will not have a time zone because none was supplied. You can override this by setting the placeholderValue prop explicitly. Values emitted from onChange will use the time zone of the placeholder value.

tsx example
import {now} from '@internationalized/date';

<Flex gap="size-150" wrap>
  <DatePicker
    label="Event date"
    granularity="second" />
  <DatePicker
    label="Event date"
    placeholderValue={now('America/New_York')}
    granularity="second" />
</Flex>

International calendars

DatePicker supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the Unicode calendar locale extension, passed to the Provider component.

Selected dates passed to onChange always use the same calendar system as the value or defaultValue prop. If no value or defaultValue is provided, then dates passed to onChange are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.

The below example displays a DatePicker in the Hindi language, using the Indian calendar. Dates emitted from onChange are in the Gregorian calendar.

tsx example
import {Provider} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(null);
  return (
    <Provider locale="hi-IN-u-ca-indian">
      <DatePicker label="Date" value={date} onChange={setDate} />
      <p>Selected date: {date?.toString()}</p>
    </Provider>
  );
}

Custom calendar systems

DatePicker also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a 4-5-4 format, where month ranges don't follow the usual Gregorian calendar. This prop is used when displaying the calendar popover component.

The createCalendar prop accepts a function that returns an instance of the interface. See the docs in @internationalized/date for an example implementation.

tsx import
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';
text
/* Detailed source-code truncated for AI context efficiency. */

HTML forms

DatePicker supports the name prop for integration with HTML forms. The value will be submitted to the server as an ISO 8601 formatted string according to the granularity of the value. For example, if the date picker allows selecting only a date then a string such as "2023-02-03" will be submitted, and if it allows selecting a time then a string such as "2023-02-03T08:45:00" will be submitted. See the Value section above for more details about the supported value types.

tsx example
<DatePicker label="Birth date" name="birthday" />

Labeling

A visual label should be provided for the DatePicker using the label prop. If the DatePicker is required, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-150" wrap>
  <DatePicker label="Birth date" />
  <DatePicker label="Birth date" isRequired necessityIndicator="icon" />
  <DatePicker label="Birth date" isRequired necessityIndicator="label" />
  <DatePicker label="Birth date" necessityIndicator="label" />
</Flex>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the DatePicker for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a DatePicker, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Events

DatePicker accepts an onChange prop which is triggered whenever the date is edited by the user. The example below uses onChange to update a separate element with a formatted version of the date in the user's locale and local time zone. This is done by converting the date to a native JavaScript Date object to pass to the formatter.

tsx example
import {getLocalTimeZone} from '@internationalized/date';
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(parseDate('1985-07-03'));
  let formatter = useDateFormatter({dateStyle: 'full'});

  return (
    <>
      <DatePicker label="Birth date" value={date} onChange={setDate} />
      <p>Selected date: {date ? formatter.format(date.toDate(getLocalTimeZone())) : '--'}</p>
    </>
  );
}

Validation

DatePicker supports the isRequired prop to ensure the user enters a value, as well as minimum and maximum values, and custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the date picker or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <DatePicker label="Appointment date" name="date" isRequired />
  {/*- end highlight -*/}
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, DatePicker displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Minimum and maximum values

The minValue and maxValue props can also be used to ensure the value is within a specific range. This example only accepts dates after today.

tsx example
import {today} from '@internationalized/date';

<Form validationBehavior="native" maxWidth="size-3000">
  <DatePicker
    label="Appointment date"
    /*- begin highlight -*/
    minValue={today(getLocalTimeZone())}
    /*- end highlight -*/
    defaultValue={parseDate('2022-02-03')} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Unavailable dates

DatePicker supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance in the calendar. In the date field, an invalid state is displayed if a user enters an unavailable date. The isDateUnavailable prop accepts a callback that is called to evaluate whether each visible date is unavailable.

This example includes multiple unavailable date ranges, e.g. dates when no appointments are available. In addition, all weekends are unavailable. The minValue prop is also used to prevent selecting dates before today.

tsx example
import {today, isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let now = today(getLocalTimeZone());
  let disabledRanges = [
    [now, now.add({days: 5})],
    [now.add({days: 14}), now.add({days: 16})],
    [now.add({days: 23}), now.add({days: 24})],
  ];

  let {locale} = useLocale();
  return (
    <DatePicker
      label="Appointment date"
      minValue={today(getLocalTimeZone())}
      /*- begin highlight -*/
      isDateUnavailable={date => isWeekend(date, locale) || disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0)}
      /*- end highlight -*/
      validationBehavior="native" />
  );
}

Custom validation

The validate function can be used to perform custom validation logic. It receives the current date value, and should return a string or array of strings representing one or more error messages if the value is invalid.

This example validates that the selected date is a weekday and not a weekend according to the current locale.

tsx example
import {isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let {locale} = useLocale();

  return (
    <Form validationBehavior="native" maxWidth="size-3000">
      <DatePicker
        label="Appointment date"
        /*- begin highlight -*/
        validate={date => date && isWeekend(date, locale) ? 'We are closed on weekends.' : null}
        /*- end highlight -*/
        defaultValue={parseDate('2023-10-28')} />
      <ButtonGroup>
        <Button type="submit" variant="primary">Submit</Button>
        <Button type="reset" variant="secondary">Reset</Button>
      </ButtonGroup>
    </Form>
  );
}

Props

Visual options

Quiet

tsx example
<DatePicker label="Birth date" isQuiet />

Disabled

tsx example
<DatePicker label="Birth date" isDisabled />

Read only

The isReadOnly boolean prop makes the DatePicker's value immutable. Unlike isDisabled, the DatePicker remains focusable.

tsx example
<DatePicker label="Birth date" value={today(getLocalTimeZone())} isReadOnly />

Label alignment and position

View guidelines

By default, the label is positioned above the DatePicker. The labelPosition prop can be used to position the label to the side. The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the DatePicker and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<DatePicker label="Birth date" labelPosition="side" labelAlign="end" />

Help text

View guidelines

Both a description and an error message can be supplied to a DatePicker. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
<Flex gap="size-100" wrap>
  <DatePicker label="Date" defaultValue={today(getLocalTimeZone())} validationState="valid" description="Select a meeting date." />
  <DatePicker label="Date" validationState="invalid" errorMessage="Empty input is not allowed." />
</Flex>

DatePicker also supports displaying the expected date format for the user's locale automatically using the showFormatHelpText prop.

tsx example
<DatePicker label="Birth date" showFormatHelpText />

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a DatePicker.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<DatePicker
  label="Appointment date"
  contextualHelp={
    <ContextualHelp variant="info">
      <Heading>Appointment changes</Heading>
      <Content>Your appointment date cannot be changed once it is scheduled.</Content>
    </ContextualHelp>
  } />

Placeholder value

When no value is set, a placeholder is shown. The format of the placeholder is influenced by the granularity and placeholderValue props. placeholderValue also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys, as well as the default month shown in the calendar popover. By default, the placeholderValue is the current date at midnight, but you can set it to a more appropriate value if needed.

tsx example
import {CalendarDate} from '@internationalized/date';

<DatePicker label="Birth date" placeholderValue={new CalendarDate(1980, 1, 1)} />

Maximum visible months

By default, the calendar popover displays a single month. The maxVisibleMonths prop allows displaying up to 3 months at a time, if screen space permits.

tsx example
<DatePicker label="Appointment date" maxVisibleMonths={3} />

Page behavior

By default, when pressing the next or previous buttons, pagination will advance by the maxVisibleMonths value. This behavior can be changed to page by single months instead, by setting pageBehavior to single.

tsx example
<DatePicker label="Appointment date" maxVisibleMonths={3} pageBehavior="single" />

Hide time zone

When a ZonedDateTime object is provided as the value of a DatePicker, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the hideTimeZone option.

tsx example
<DatePicker
  label="Appointment time"
  defaultValue={parseZonedDateTime('2022-11-07T10:45[America/Los_Angeles]')}
  hideTimeZone />

Hour cycle

By default, DatePicker displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the hourCycle prop if needed for a specific usecase. This example forces the DatePicker to use 24-hour time, regardless of the locale.

tsx example
<DatePicker
  label="Appointment time"
  granularity="minute"
  hourCycle={24} />

Custom first day of week

By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the firstDayOfWeek prop to 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.

tsx example
<DatePicker label="Appointment date" firstDayOfWeek="mon" />

Testing

The DatePicker features an overlay that transitions in and out of the page as it is opened and closed. Depending on
your configuration, this overlay may render as a tray or a popover. Please see the following sections in the
testing docs for more information on how to handle these behaviors in your test suite.

Timers

Desktop vs Mobile

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Datepicker DateRangePicker (`packages/@adobe/react-spectrum/docs/datepicker/DateRangePicker.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/datepicker';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/datepicker/package.json';

jsx import
import {DateRangePicker} from '@react-spectrum/datepicker';
import {Flex} from '@react-spectrum/layout';

category: Date and Time keywords: [input, form, field, date, time]

DateRangePicker

{docs.exports.DateRangePicker.description}

<HeaderInfo
packageData={packageData}
componentNames={['DateRangePicker']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<DateRangePicker label="Date range" />

Value

A DateRangePicker displays a placeholder by default. An initial, uncontrolled value can be provided to the DateRangePicker using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Date ranges are objects with start and end properties containing date values, which are provided using objects in the @internationalized/date package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. DateRangePicker supports values of the following types:

  • – a date without any time components. May be parsed from a string representation using the function. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event.
  • – a date with a time, but not in any specific time zone. May be parsed from a string representation using the function. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a ZonedDateTime.
  • – a date with a time in a specific time zone. May be parsed from a string representation using the , , or functions. Use this type to represent an exact moment in time at a particular location on Earth.
tsx example
import {parseDate} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState({
    start: parseDate('2020-02-03'),
    end: parseDate('2020-02-08')
  });

  return (
    <Flex gap="size-150" wrap>
      <DateRangePicker
        label="Date range (uncontrolled)"
        defaultValue={{
          start: parseDate('2020-02-03'),
          end: parseDate('2020-02-08')
        }} />
      <DateRangePicker
        label="Date range (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

Time zones

DateRangePicker is time zone aware when objects are provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

In most cases, your data will come from and be sent to a server as an ISO 8601 formatted string. @internationalized/date includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store.

  • – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. "2021-11-07T00:45[America/Los_Angeles]" or "2021-11-07T00:45-07:00[America/Los_Angeles]"). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.
  • – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. "2021-11-07T07:45:00Z"), or stored with a particular offset (e.g. "2021-11-07T07:45:00-07:00"). A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone.
  • – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for parseAbsolute, and accepts the same formats.
tsx example
import {parseZonedDateTime} from '@internationalized/date';

<DateRangePicker
  label="Date range"
  defaultValue={{
    start: parseZonedDateTime('2022-11-07T00:45[America/Los_Angeles]'),
    end: parseZonedDateTime('2022-11-08T11:15[America/Los_Angeles]')
  }} />

DateRangePicker displays times in the time zone included in the ZonedDateTime object. The above example is always displayed in Pacific Standard Time because the America/Los_Angeles time zone identifier is provided. @internationalized/date includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below.

tsx example
import {parseAbsoluteToLocal} from '@internationalized/date';

<DateRangePicker
  label="Date range"
  defaultValue={{
    start: parseAbsoluteToLocal('2021-11-07T07:45:00Z'),
    end: parseAbsoluteToLocal('2021-11-08T14:25:00Z')
  }}
/>

Granularity

The granularity prop allows you to control the smallest unit that is displayed by a DateRangePicker. By default, CalendarDate values are displayed with "day" granularity (year, month, and day), and CalendarDateTime and ZonedDateTime values are displayed with "minute" granularity. More granular time values can be displayed by setting the granularity prop to "second".

In addition, when a value with a time is provided but you wish to only display the date, you can set the granularity to "day". This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two DateRangePickers are synchronized with the same value, but display different granularities.

tsx example
function Example() {
  let [date, setDate] = React.useState({
    start: parseAbsoluteToLocal('2021-04-07T18:45:22Z'),
    end: parseAbsoluteToLocal('2021-04-08T20:00:00Z')
  });

  return (
    <Flex gap="size-150" wrap>
      <DateRangePicker
        label="Date and time range"
        granularity="second"
        value={date}
        onChange={setDate} />
      <DateRangePicker
        label="Date range"
        granularity="day"
        value={date}
        onChange={setDate} />
    </Flex>
  );
}

If no value or defaultValue prop is passed, then the granularity prop also affects which type of value is emitted from the onChange event. Note that by default, time values will not have a time zone because none was supplied. You can override this by setting the placeholderValue prop explicitly. Values emitted from onChange will use the time zone of the placeholder value.

tsx example
import {now} from '@internationalized/date';

<Flex gap="size-150" wrap>
  <DateRangePicker
    label="Date range"
    granularity="second" />
  <DateRangePicker
    label="Date range"
    placeholderValue={now('America/New_York')}
    granularity="second" />
</Flex>

International calendars

DateRangePicker supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the Unicode calendar locale extension, passed to the Provider component.

Selected dates passed to onChange always use the same calendar system as the value or defaultValue prop. If no value or defaultValue is provided, then dates passed to onChange are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.

The below example displays a DateRangePicker in the Hindi language, using the Indian calendar. Dates emitted from onChange are in the Gregorian calendar.

tsx example
import {Provider} from '@adobe/react-spectrum';

function Example() {
  let [range, setRange] = React.useState(null);
  return (
    <Provider locale="hi-IN-u-ca-indian">
      <DateRangePicker label="Date range" value={range} onChange={setRange} />
      <p>Start date: {range?.start.toString()}</p>
      <p>End date: {range?.end.toString()}</p>
    </Provider>
  );
}

Custom calendar systems

DateRangePicker also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a 4-5-4 format, where month ranges don't follow the usual Gregorian calendar. This prop is used when displaying the range calendar popover component.

The createCalendar prop accepts a function that returns an instance of the interface. See the docs in @internationalized/date for an example implementation.

tsx import
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';
text
/* Detailed source-code truncated for AI context efficiency. */

HTML forms

DateRangePicker supports the startName and endName props for integration with HTML forms. The values will be submitted to the server as ISO 8601 formatted strings according to the granularity of the value. For example, if the date range picker allows selecting only dates then strings such as "2023-02-03" will be submitted, and if it allows selecting times then strings such as "2023-02-03T08:45:00" will be submitted. See the Value section above for more details about the supported value types.

tsx example
<DateRangePicker label="Trip dates" startName="startDate" endName="endDate" />

Labeling

A visual label should be provided for the DateRangePicker using the label prop. If the DateRangePicker is required, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-150" wrap>
  <DateRangePicker label="Date range" />
  <DateRangePicker label="Date range" isRequired necessityIndicator="icon" />
  <DateRangePicker label="Date range" isRequired necessityIndicator="label" />
  <DateRangePicker label="Date range" necessityIndicator="label" />
</Flex>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the DateRangePicker for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a DateRangePicker, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Events

DateRangePicker accepts an onChange prop which is triggered whenever the start or end date is edited by the user. The example below uses onChange to update a separate element with a formatted version of the date range in the user's locale and local time zone. This is done by converting the dates to native JavaScript Date objects to pass to the formatter.

tsx example
import {getLocalTimeZone} from '@internationalized/date';
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [range, setRange] = React.useState({
    start: parseDate('2020-07-03'),
    end: parseDate('2020-07-10')
  });
  let formatter = useDateFormatter({dateStyle: 'long'});

  return (
    <>
      <DateRangePicker label="Date range" value={range} onChange={setRange} />
      <p>Selected date: {range ? formatter.formatRange(range.start.toDate(getLocalTimeZone()), range.end.toDate(getLocalTimeZone())) : '--'}</p>
    </>
  );
}

Validation

DateRangePicker supports the isRequired prop to ensure the user enters a value, as well as minimum and maximum values, and custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the date range picker or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <DateRangePicker label="Date range" startName="startDate" endName="endDate" isRequired />
  {/*- end highlight -*/}
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, DateRangePicker displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Minimum and maximum values

The minValue and maxValue props can also be used to ensure the value is within a specific range. DateRangePicker also validates that the end date is after the start date. This example only accepts dates after today.

tsx example
import {today} from '@internationalized/date';

<Form validationBehavior="native" maxWidth="size-3000">
  <DateRangePicker
    label="Trip dates"
    /*- begin highlight -*/
    minValue={today(getLocalTimeZone())}
    /*- end highlight -*/
    defaultValue={{
      start: parseDate('2022-02-03'),
      end: parseDate('2022-05-03')
    }} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Custom validation

The validate function can be used to perform custom validation logic. It receives the current date range, and should return a string or array of strings representing one or more error messages if the value is invalid.

This example validates that the selected date range is a maximum of 1 week in duration.

tsx example
<Form validationBehavior="native" maxWidth="size-3000">
  <DateRangePicker
    label="Trip dates"
    /*- begin highlight -*/
    validate={range => range?.end.compare(range.start) > 7 ? 'Maximum stay duration is 1 week.' : null}
    /*- end highlight -*/
    defaultValue={{
      start: today(getLocalTimeZone()),
      end: today(getLocalTimeZone()).add({ weeks: 1, days: 3 })
    }} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Unavailable dates

DateRangePicker supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance in the calendar. The isDateUnavailable prop accepts a callback that is called to evaluate whether each visible date is unavailable.

Note that by default, users may not select non-contiguous ranges, i.e. ranges that contain unavailable dates within them. Once a start date is selected in the calendar, enabled dates will be restricted to subsequent dates until an unavailable date is hit. While this is handled automatically in the calendar, additional validation logic must be provided to ensure an invalid state is displayed in the date field. This can be achieved using the validationState prop. See below for an example of how to allow non-contiguous ranges.

This example includes multiple unavailable date ranges, e.g. dates when a rental house is not available. The minValue prop is also used to prevent selecting dates before today. The validationState prop is used to mark selected date ranges with unavailable dates in the middle as invalid.

tsx example
import {today} from '@internationalized/date';

function Example() {
  let now = today(getLocalTimeZone());
  let disabledRanges = [
    [now, now.add({days: 5})],
    [now.add({days: 14}), now.add({days: 16})],
    [now.add({days: 23}), now.add({days: 24})],
  ];

  return (
    <DateRangePicker
      label="Trip dates"
      minValue={today(getLocalTimeZone())}
      /*- begin highlight -*/
      isDateUnavailable={date => disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0)}
      validate={value => disabledRanges.some(interval => value && value.end.compare(interval[0]) >= 0 && value.start.compare(interval[1]) <= 0) ? 'Selected date range may not include unavailable dates.' : null}
      /*- end highlight -*/
      validationBehavior="native" />
  );
}

Non-contiguous ranges

The allowsNonContiguousRanges prop enables a range to be selected even if there are unavailable dates in the middle. The value emitted in the onChange event will still be a single range with a start and end property, but unavailable dates will not be displayed as selected. It is up to applications to split the full selected range into multiple as needed for business logic.

This example prevents selecting weekends, but allows selecting ranges that span multiple weeks.

tsx example
import {isWeekend} from '@internationalized/date';
import {useLocale} from '@adobe/react-spectrum';

function Example() {
  let {locale} = useLocale();

  return <DateRangePicker label="Time off request" isDateUnavailable={date => isWeekend(date, locale)} allowsNonContiguousRanges />
}

Props

Visual options

Quiet

tsx example
<DateRangePicker label="Date range" isQuiet />

Disabled

tsx example
<DateRangePicker label="Date range" isDisabled />

Read only

The isReadOnly boolean prop makes the DateRangePicker's value immutable. Unlike isDisabled, the DateRangePicker remains focusable.

tsx example
<DateRangePicker label="Date range" value={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({weeks: 1})}} isReadOnly />

Label alignment and position

View guidelines

By default, the label is positioned above the DateRangePicker. The labelPosition prop can be used to position the label to the side. The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the DateRangePicker and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<DateRangePicker label="Date range" labelPosition="side" labelAlign="end" />

Help text

View guidelines

Both a description and an error message can be supplied to a DateRangePicker. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
<Flex gap="size-100" wrap>
  <DateRangePicker label="Date range" defaultValue={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({weeks: 1})}} validationState="valid" description="Select your trip dates." />
  <DateRangePicker label="Date range" validationState="invalid" errorMessage="Empty input is not allowed." />
</Flex>

DateRangePicker also supports displaying the expected date format for the user's locale automatically using the showFormatHelpText prop.

tsx example
<DateRangePicker label="Date range" showFormatHelpText />

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a DateRangePicker.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<DateRangePicker
  label="Trip dates"
  contextualHelp={
    <ContextualHelp variant="info">
      <Heading>Date changes</Heading>
      <Content>Your trip dates cannot be changed once scheduled.</Content>
    </ContextualHelp>
  } />

Placeholder value

When no value is set, a placeholder is shown. The format of the placeholder is influenced by the granularity and placeholderValue props. placeholderValue also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys, as well as the default month shown in the calendar popover. By default, the placeholderValue is the current date at midnight, but you can set it to a more appropriate value if needed.

tsx example
import {CalendarDate} from '@internationalized/date';

<DateRangePicker label="Date range" placeholderValue={new CalendarDate(1980, 1, 1)} />

Maximum visible months

By default, the calendar popover displays a single month. The maxVisibleMonths prop allows displaying up to 3 months at a time, if screen space permits.

tsx example
<DateRangePicker label="Date range" maxVisibleMonths={3} />

Page behavior

By default, when pressing the next or previous buttons, pagination will advance by the maxVisibleMonths value. This behavior can be changed to page by single months instead, by setting pageBehavior to single.

tsx example
<DateRangePicker label="Date range" maxVisibleMonths={3} pageBehavior="single" />

Hide time zone

When ZonedDateTime objects are provided as the value of a DateRangePicker, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the hideTimeZone option.

tsx example
<DateRangePicker
  label="Date range"
  defaultValue={{
    start: parseZonedDateTime('2022-11-07T10:45[America/Los_Angeles]'),
    end: parseZonedDateTime('2022-11-08T19:45[America/Los_Angeles]')
  }}
  hideTimeZone />

Hour cycle

By default, DateRangePicker displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the hourCycle prop if needed for a specific usecase. This example forces the DateRangePicker to use 24-hour time, regardless of the locale.

tsx example
<DateRangePicker
  label="Date range"
  granularity="minute"
  hourCycle={24} />

Custom first day of week

By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the firstDayOfWeek prop to 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.

tsx example
<DateRangePicker label="Date range" firstDayOfWeek="mon" />

Testing

The DateRangePicker features an overlay that transitions in and out of the page as it is opened and closed. Depending on
your configuration, this overlay may render as a tray or a popover. Please see the following sections in the
testing docs for more information on how to handle these behaviors in your test suite.

Timers

Desktop vs Mobile

Please also refer to React Spectrum's test suite if you find that the above
isn't sufficient when resolving issues in your own test cases.


Packages @Adobe React Spectrum Docs Datepicker TimeField (`packages/@adobe/react-spectrum/docs/datepicker/TimeField.mdx`)

{/* Copyright 2022 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/datepicker';
import i18nDocs from 'docs:@internationalized/date';
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-spectrum/datepicker/package.json';

jsx import
import {TimeField} from '@react-spectrum/datepicker';
import {Flex} from '@react-spectrum/layout';

category: Date and Time keywords: [input, form, field, time]

TimeField

{docs.exports.TimeField.description}

<HeaderInfo
packageData={packageData}
componentNames={['TimeField']}
sourceData={[]}
since="3.19.0" />

Example

tsx example
<TimeField label="Event time" />

Value

A TimeField displays a placeholder by default. An initial, uncontrolled value can be provided to the TimeField using the defaultValue prop. Alternatively, a controlled value can be provided using the value prop.

Time values are provided using objects in the @internationalized/date package. This library handles correct international date and time manipulation across calendars, time zones, and other localization concerns.

TimeField only supports selecting times, but values with date components are also accepted. By default, TimeField will emit objects in the onChange event, but if a or object is passed as the value or defaultValue, values of that type will be emitted, changing only the time and preserving the date components.

tsx example
import {Time} from '@internationalized/date';

function Example() {
  let [value, setValue] = React.useState(new Time(11, 45));

  return (
    <Flex gap="size-150" wrap>
      <TimeField
        label="Time (uncontrolled)"
        defaultValue={new Time(11, 45)} />
      <TimeField
        label="Time (controlled)"
        value={value}
        onChange={setValue} />
    </Flex>
  );
}

Time values may also be parsed from strings using the function. This accepts ISO 8601 formatted time strings such as "04:45:23.123". The toString method of a Time object can also be used to convert a time object to a string.

Time zones

TimeField is time zone aware when a object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

In most cases, your data will come from and be sent to a server as an ISO 8601 formatted string. @internationalized/date includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store.

  • – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. "2021-11-07T00:45[America/Los_Angeles]" or "2021-11-07T00:45-07:00[America/Los_Angeles]"). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location.
  • – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. "2021-11-07T07:45:00Z"), or stored with a particular offset (e.g. "2021-11-07T07:45:00-07:00"). A time zone identifier, e.g. America/Los_Angeles, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone.
  • – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for parseAbsolute, and accepts the same formats.
tsx example
import {parseZonedDateTime} from '@internationalized/date';

<TimeField
  label="Event time"
  defaultValue={parseZonedDateTime('2022-11-07T00:45[America/Los_Angeles]')} />

TimeField displays times in the time zone included in the ZonedDateTime object. The above example is always displayed in Pacific Standard Time because the America/Los_Angeles time zone identifier is provided. @internationalized/date includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below.

tsx example
import {parseAbsoluteToLocal} from '@internationalized/date';

<TimeField
  label="Event time"
  defaultValue={parseAbsoluteToLocal('2021-11-07T07:45:00Z')}
/>

Granularity

The granularity prop allows you to control the smallest unit that is displayed by a TimeField. By default, times are displayed with "minute" granularity. More granular time values can be displayed by setting the granularity prop to "second".

tsx example
<TimeField
  label="Event time"
  granularity="second"
  defaultValue={parseAbsoluteToLocal('2021-04-07T18:45:22Z')} />

HTML forms

TimeField supports the name prop for integration with HTML forms. The value will be submitted to the server as an ISO 8601 formatted string, e.g. "08:45:00".

tsx example
<TimeField label="Meeting time" name="meetingTime" />

Labeling

A visual label should be provided for the TimeField using the label prop. If the TimeField is required, the isRequired and necessityIndicator props can be used to show a required state.

tsx example
<Flex gap="size-150" wrap>
  <TimeField label="Event time" />
  <TimeField label="Event time" isRequired necessityIndicator="icon" />
  <TimeField label="Event time" isRequired necessityIndicator="label" />
  <TimeField label="Event time" necessityIndicator="label" />
</Flex>

Accessibility

If a visible label isn't specified, an aria-label must be provided to the TimeField for
accessibility. If the field is labeled by a separate element, an aria-labelledby prop must be provided using
the id of the labeling element instead.

Internationalization

In order to internationalize a TimeField, a localized string should be passed to the label or aria-label prop.
When the necessityIndicator prop is set to "label", a localized string will be provided for "(required)" or "(optional)" automatically.

Events

TimeField accepts an onChange prop which is triggered whenever the time is edited by the user. The example below uses onChange to update a separate element with a formatted version of the date in the user's locale and local time zone. This is done by converting the date to a native JavaScript Date object to pass to the formatter. The TimeField allows editing the time components while keeping the date fixed.

tsx example
import {useDateFormatter} from '@adobe/react-spectrum';

function Example() {
  let [date, setDate] = React.useState(parseAbsoluteToLocal('2021-04-07T18:45:22Z'));
  let formatter = useDateFormatter({dateStyle: 'long', timeStyle: 'long'});

  return (
    <>
      <TimeField label="Time" value={date} onChange={setDate} />
      <p>Selected date and time: {(date?.toDate && formatter.format(date.toDate())) || (date && date.toString()) || '--'}</p>
    </>
  );
}

Validation

TimeField supports the isRequired prop to ensure the user enters a value, as well as minimum and maximum values, and custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

When the Form component has the validationBehavior="native" prop, validation errors block form submission and are displayed as help text automatically. Errors are displayed when the user blurs the time field or submits the form.

tsx example
import {Form, ButtonGroup, Button} from '@adobe/react-spectrum';

<Form validationBehavior="native" maxWidth="size-3000">
  {/*- begin highlight -*/}
  <TimeField label="Meeting time" name="time" isRequired />
  {/*- end highlight -*/}
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

By default, TimeField displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Minimum and maximum values

The minValue and maxValue props can also be used to ensure the value is within a specific range. This example only accepts times between 9 AM and 5 PM.

tsx example
<Form validationBehavior="native" maxWidth="size-3000">
  <TimeField
    label="Meeting time"
    /*- begin highlight -*/
    minValue={new Time(9)}
    maxValue={new Time(17)}
    /*- end highlight -*/
    defaultValue={new Time(8)} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Custom validation

The validate function can be used to perform custom validation logic. It receives the current field value, and should return a string or array of strings representing one or more error messages if the value is invalid.

This example validates that the selected time is on a 15 minute increment.

tsx example
<Form validationBehavior="native" maxWidth="size-3000">
  <TimeField
    label="Meeting time"
    /*- begin highlight -*/
    validate={time => time?.minute % 15 !== 0 ? 'Meetings start every 15 minutes.' : null}
    /*- end highlight -*/
    defaultValue={new Time(9, 25)} />
  <ButtonGroup>
    <Button type="submit" variant="primary">Submit</Button>
    <Button type="reset" variant="secondary">Reset</Button>
  </ButtonGroup>
</Form>

Props

Visual options

Quiet

tsx example
<TimeField label="Event time" isQuiet />

Disabled

tsx example
<TimeField label="Event time" isDisabled />

Read only

The isReadOnly boolean prop makes the TimeField's value immutable. Unlike isDisabled, the TimeField remains focusable.

tsx example
<TimeField label="Event time" value={new Time(11)} isReadOnly />

Label alignment and position

View guidelines

By default, the label is positioned above the TimeField. The labelPosition prop can be used to position the label to the side. The labelAlign prop can be used to align the label as "start" or "end". For left-to-right (LTR) languages, "start" refers to the left most edge of the TimeField and "end" refers to the right most edge. For right-to-left (RTL) languages, this is flipped.

tsx example
<TimeField label="Event time" labelPosition="side" labelAlign="end" />

Help text

View guidelines

Both a description and an error message can be supplied to a TimeField. The description is always visible unless the validationState is “invalid” and an error message is provided. The error message can be used to help the user fix their input quickly and should be specific to the detected error. All strings should be localized.

tsx example
<Flex gap="size-100" wrap>
  <TimeField label="Time" defaultValue={new Time(9)} validationState="valid" description="Select a meeting time." />
  <TimeField label="Time" validationState="invalid" errorMessage="Empty input is not allowed." />
</Flex>

Contextual help

A ContextualHelp element may be placed next to the label to provide additional information or help about a TimeField.

tsx example
import {Content, ContextualHelp, Heading} from '@adobe/react-spectrum';

<TimeField
  label="Appointment time"
  contextualHelp={
    <ContextualHelp variant="info">
      <Heading>Appointment changes</Heading>
      <Content>Your appointment time cannot be changed once it is scheduled.</Content>
    </ContextualHelp>
  } />

Placeholder value

When no value is set, a placeholder is shown. The format of the placeholder is influenced by the granularity and placeholderValue props. placeholderValue also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys. By default, the placeholderValue is midnight, but you can set it to a more appropriate value if needed.

tsx example
<TimeField label="Appointment time" placeholderValue={new Time(9)} />

Hide time zone

When a ZonedDateTime object is provided as the value of a TimeField, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the hideTimeZone option.

tsx example
<TimeField
  label="Appointment time"
  defaultValue={parseZonedDateTime('2022-11-07T10:45[America/Los_Angeles]')}
  hideTimeZone />

Hour cycle

By default, TimeField displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the hourCycle prop if needed for a specific usecase. This example forces the TimeField to use 24-hour time, regardless of the locale.

tsx example
<TimeField
  label="Appointment time"
  hourCycle={24} />

Packages @Adobe React Spectrum Docs Dialog AlertDialog (`packages/@adobe/react-spectrum/docs/dialog/AlertDialog.mdx`)

{/* Copyright 2020 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 '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/dialog';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/dialog/package.json';

jsx import
import {ActionButton} from '@react-spectrum/button';
import {AlertDialog, DialogTrigger} from '@react-spectrum/dialog';

category: Overlays keywords: [alert dialog]

AlertDialog

{docs.exports.AlertDialog.description}

<HeaderInfo
packageData={packageData}
componentNames={['AlertDialog', 'DialogTrigger']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/alert-dialog/'}
]}
since="3.0.0" />

Example

tsx example
<DialogTrigger>
  <ActionButton>Save</ActionBu