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.
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.
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.
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:
yarn
yarn storybookBuilding 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
CalendarDatemethods, 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.
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-06Packages @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.
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.
<Highlights items={[
{
title: 'React Spectrum',
url: './react-spectrum/index.html',
urlText: <>Explore
},
{
title: 'React Aria',
url: './react-aria/index.html',
urlText: <>Learn more
},
{
title: 'React Stately',
url: './react-stately/index.html',
urlText: <>Get Started
},
{
title: 'Internationalized',
url: './internationalized/index.html',
urlText: <>Show
}
]} />
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.
yarn add @adobe/react-spectrumIf 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.
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.
<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).
/* Detailed source-code truncated for AI context efficiency. */This enables dates to be converted between calendar systems.
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.
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
import {parseDate} from '@internationalized/date';
let date = parseDate('2022-02-03');Today's date can be retrieved using the
Note: the local time zone is cached after the first call. You can reset it by calling
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.
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 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.
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.
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
This example converts a Gregorian date to a Hebrew one.
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 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.
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-02Adding 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
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.
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-16Setting 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.
let date = new CalendarDate(2022, 2, 3);
date.set({day: 100}); // 2022-02-28
date.set({month: 20}); // 2022-12-03Cycling 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.
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-01The 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.
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-03Note 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.
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
Note: the local time zone is cached after the first call. You can reset it by calling
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 ZonedDateTime.
Use the CalendarDate to a CalendarDateTime. By default, the time will be set to midnight. You can also pass a
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:00A
Use the 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.
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.
let a = new CalendarDate(2022, 2, 3);
let b = new CalendarDate(2022, 3, 4);
a.compare(b) < 0; // true
b.compare(a) > 0; // trueIn 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}
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)); // trueA 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}
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)); // falseStart 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.
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-31You 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.
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31Day of week
The
import {getDayOfWeek} from '@internationalized/date';
let date = new CalendarDate(2022, 2, 6); // a Sunday
getDayOfWeek(date, 'en-US'); // 0
getDayOfWeek(date, 'fr-FR'); // 6You 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.
getDayOfWeek(date, 'en-US', 'mon'); // 6Weekdays and weekends
The
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'); // falseWeeks in month
The
import {getWeeksInMonth} from '@internationalized/date';
let date = new CalendarDate(2021, 1, 1);
getWeeksInMonth(date, 'en-US'); // 6
getWeeksInMonth(date, 'fr-FR'); // 5You 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.
getWeeksInMonth(date, 'en-US', 'mon'); // 5Packages @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.
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
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.
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 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.
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.
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
This example converts a Gregorian date to a Hebrew one.
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 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.
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:15Adding 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
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.
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:15Setting 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.
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:45Cycling 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.
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:59Note 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.
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:15By 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.
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:00Conversion
To a string
CalendarDateTime objects can be converted to an ISO 8601 formatted string using the toString method.
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 CalendarDateTime to a CalendarDate.
import {toCalendarDate} from '@internationalized/date';
let date = new CalendarDateTime(2022, 2, 3, 9, 45);
toCalendarDate(date); // 2022-02-03Use the CalendarDateTime to a Time.
import {toTime} from '@internationalized/date';
let date = new CalendarDateTime(2022, 2, 3, 9, 45);
toTime(date); // 09:45:00To a ZonedDateTime
A CalendarDateTime can be converted to a local time in a specific time zone using the
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
// 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
Note: the local time zone is cached after the first call. You can reset it by calling
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.
// 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.
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; // trueIn 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}
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)); // trueA 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}
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)); // falseStart 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.
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:45You 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.
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31T09:45Day of week
The
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'); // 6You 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.
getDayOfWeek(date, 'en-US', 'mon'); // 6Weekdays and weekends
The
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'); // falseWeeks in month
The
import {getWeeksInMonth} from '@internationalized/date';
let date = new CalendarDateTime(2021, 1, 1, 8, 30);
getWeeksInMonth(date, 'en-US'); // 6
getWeeksInMonth(date, 'fr-FR'); // 5You 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.
getWeeksInMonth(date, 'en-US', 'mon'); // 5Packages @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
formatRangeandformatRangeToPartsmethods for older browsers. - A workaround for a bug involving the
hour12option in Chrome and and the ECMAScript spec. - A workaround for a bug involving the
hourCycleoption 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.
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
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.
let time = new Time(9, 45);
time.toString(); // '09:45:00'Interface
Manipulating times
Adding and subtracting durations
A 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.
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:01Adding 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
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.
let time = new Time(9, 45);
time.set({hour: 12}); // 12:45
time.set({minute: 5}); // 9:05Setting a field to a value that is outside the valid range will cause it to be constrained.
let time = new Time(9, 45);
time.set({hour: 36}); // 23:45
time.set({minute: 75}); // 09:59Cycling 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.
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:59The 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.
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:15By 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.
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:00Conversion
To a string
Time objects can be converted to an ISO 8601 formatted string using the toString method.
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
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:00A
The CalendarDateTime to a ZonedDateTime.
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.
let a = new Time(9, 45);
let b = new Time(12, 20);
a.compare(b) < 0; // true
b.compare(a) > 0; // truePackages @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".
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.
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 ZonedDateTimefrom aDateobject. 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 ZonedDateTimefrom aDateobject. 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 ZonedDateTimefrom 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.
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
Note: the local time zone is cached after the first call. You can reset it by calling
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.
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 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.
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.
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
This example converts a Gregorian date to a Hebrew one.
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 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.
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.
// 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.
// 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
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.
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.
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.
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
// 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.
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.
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.
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.
// 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.
// 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.
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.
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.
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 ZonedDateTime to a CalendarDate.
import {toCalendarDate} from '@internationalized/date';
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toCalendarDate(date); // 2022-02-03Use the ZonedDateTime to a Time.
import {toTime} from '@internationalized/date';
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toTime(date); // 09:45To 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 ZonedDateTime to a CalendarDateTime.
import {toCalendarDateTime} from '@internationalized/date';
let date = parseZonedDateTime('2022-02-03T09:45[America/Los_Angeles]');
toCalendarDateTime(date); // 2022-02-03T09:45:00Between time zones
A ZonedDateTime can be converted to a different time zone using the
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.
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; // trueIn 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}
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]'),
); // falseA 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.
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.
startOfWeek(date, 'en-US', 'mon'); // 2022-01-31T09:45[America/Los_Angeles]Day of week
The
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'); // 6You 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.
getDayOfWeek(date, 'en-US', 'mon'); // 6Weekdays and weekends
The
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'); // falseWeeks in month
The
import {getWeeksInMonth} from '@internationalized/date';
let date = parseZonedDateTime('2023-01-01T08:30[America/Los_Angeles]');
getWeeksInMonth(date, 'en-US'); // 5
getWeeksInMonth(date, 'fr-FR'); // 6You 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.
getWeeksInMonth(date, 'en-US', 'mon'); // 6Packages @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.
import {NumberParser} from '@internationalized/number';
let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.parse('10 in'); // 10Interface
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.
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'); // NaNPercentages
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.
let parser = new NumberParser('en-US', {
style: 'percent',
maximumFractionDigits: 2
});
parser.parse('45%'); // 0.45
parser.parse('62.3333%'); // 0.6233Currency 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.
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.
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.
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'); // falseDetecting 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.
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
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
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
<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';
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
<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
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]
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
<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.
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.
<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
<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
<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]
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
<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.
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
<Disclosure isDisabled>
<DisclosureTitle>System Requirements</DisclosureTitle>
<DisclosurePanel>
Details about system requirements here.
</DisclosurePanel>
</Disclosure>Expanded
<Disclosure defaultExpanded>
<DisclosureTitle>System Requirements</DisclosureTitle>
<DisclosurePanel>
Details about system requirements here.
</DisclosurePanel>
</Disclosure>Quiet
<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';
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
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.
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.
/* 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.
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.
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';
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
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 key prop. Alternatively, a function that returns Item elements is also supported. See the collection components docs for more details about this API.
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.
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.
<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.
<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.
<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.
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.
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.
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.
<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.
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.
<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 thebuttonLabelBehavior prop to "collapse". The labels will be automatically shown in tooltips when collapsed.
<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.
<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
<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.
<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.
<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.
<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.
<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.
<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.
<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><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.
<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';
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
<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.
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.
<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><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><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.
Links
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.
<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
<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.
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.
<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
<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.
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.
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.
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.
<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><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.
<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
<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
<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
<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
<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>Menu direction
<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.
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';
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
<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
<Avatar src="https://i.imgur.com/kJOwAdv.png" alt="default Adobe avatar" isDisabled />Size
Sizes can either be one of the predefined avatar size variables, or a custom size in pixels.
<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';
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
<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.
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
When badges have a semantic meaning, they should use semantic colors.
Use the appropriate color to indicate status as follows.
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.
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';
import {Breadcrumbs, Item} from '@react-spectrum/breadcrumbs';
import {View} from '@react-spectrum/view';category: Navigation keywords: [collections, dropdown]
Breadcrumbs
{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
<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 <
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
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.
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>
);
}Links
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.
<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
Small
<Breadcrumbs size="S">
<Item key="home">Home</Item>
<Item key="trendy">Trendy</Item>
</Breadcrumbs>Medium
<Breadcrumbs size="M">
<Item key="home">Home</Item>
<Item key="trendy">Trendy</Item>
</Breadcrumbs>Large (default)
<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.
<Breadcrumbs isMultiline>
<Item key="home">Home</Item>
<Item key="trendy">Trendy</Item>
<Item key="march 2020 assets">March 2020 Assets</Item>
</Breadcrumbs>Root context
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.
<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.
<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)
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.
<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><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';
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
<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.
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.
<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.
function Example() {
let [count, setCount] = React.useState(0);
return (
<ActionButton onPress={() => setCount(c => c + 1)}>{count} Edits</ActionButton>
);
}Props
Visual options
Quiet
<ActionButton isQuiet>Action!</ActionButton>Disabled
<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.
<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';
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
<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.
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.
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.
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
<Flex wrap gap="size-250">
<Button variant="accent" style="fill">Save</Button>
<Button variant="accent" style="outline">Save</Button>
</Flex>Primary
<Flex wrap gap="size-250">
<Button variant="primary" style="fill">Save</Button>
<Button variant="primary" style="outline">Save</Button>
</Flex>Secondary
<Flex wrap gap="size-250">
<Button variant="secondary" style="fill">Save</Button>
<Button variant="secondary" style="outline">Save</Button>
</Flex>Negative
<Flex wrap gap="size-250">
<Button variant="negative" style="fill">Save</Button>
<Button variant="negative" style="outline">Save</Button>
</Flex>Static color
<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
<Button variant="accent" isDisabled>Save</Button>Icon only
<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';
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
<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.
<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.
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
<LogicButton variant="or" marginEnd="20px">Or</LogicButton>
<LogicButton variant="and">And</LogicButton>Disabled
<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';
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
<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.
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.
<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.
function Example() {
let [isSelected, setSelected] = React.useState(false);
return (
<ToggleButton
isEmphasized
isSelected={isSelected}
onChange={setSelected}
aria-label="Pin">
<Pin />
</ToggleButton>
);
}Props
Visual options
Quiet
<ToggleButton isQuiet>Pin</ToggleButton>Disabled
<ToggleButton isDisabled>Pin</ToggleButton>Emphasized
<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.
<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
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
Setting this to 'vertical' will prevent any dynamic orientation changes, the ButtonGroup will remain in vertical regardless of the available width.
<ButtonGroup orientation="vertical">
<Button variant="secondary">No, thanks</Button>
<Button variant="secondary">Remind me later</Button>
<Button variant="primary">Rate Now</Button>
</ButtonGroup>Alignment
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.
<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><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
<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';
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
<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 onChange event, but if a value or defaultValue, values of that type will be emitted, changing only the date and preserving the time components.
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.
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
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';/* 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.
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.
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.
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.
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
<Calendar aria-label="Event date" isDisabled />Read only
The isReadOnly boolean prop makes the Calendar's value immutable. Unlike isDisabled, the Calendar remains focusable.
<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.
<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.
<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'.
<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';
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
<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 onChange event, but if a value or defaultValue, values of that type will be emitted, changing only the date and preserving the time components.
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.
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
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';/* 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.
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.
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.
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.
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.
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
<RangeCalendar aria-label="Trip dates" isDisabled />Read only
The isReadOnly boolean prop makes the RangeCalendar's value immutable. Unlike isDisabled, the RangeCalendar remains focusable.
<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.
<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.
<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'.
<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';
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
<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.
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
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.
<Checkbox isIndeterminate>Subscribe</Checkbox>HTML forms
Checkbox supports the name and value props for integration with HTML forms.
<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.
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.
<Checkbox isInvalid>I accept the terms and conditions</Checkbox>Props
Visual options
Disabled
<Checkbox isDisabled>Subscribe</Checkbox>Emphasized
<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';
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
<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 thevalue prop on each item.
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.
<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.
<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.
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.
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.
<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.
<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.
<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
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.
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.
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
<CheckboxGroup label="Favorite sports" isDisabled>
<Checkbox value="soccer">Soccer</Checkbox>
<Checkbox value="baseball">Baseball</Checkbox>
<Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup><CheckboxGroup label="Favorite sports">
<Checkbox value="soccer">Soccer</Checkbox>
<Checkbox value="baseball" isDisabled>Baseball</Checkbox>
<Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>Read only
The isReadOnly prop makes the selection immutable. Unlike isDisabled, the CheckboxGroup remains focusable.
See the MDN docs for more information.
<CheckboxGroup label="Favorite sports" defaultValue={['baseball']} isReadOnly>
<Checkbox value="soccer">Soccer</Checkbox>
<Checkbox value="baseball">Baseball</Checkbox>
<Checkbox value="basketball">Basketball</Checkbox>
</CheckboxGroup>Emphasized
<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;
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
<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 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
so that value's type remains consistent.
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.
<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 oraria-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 ownaria-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.
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 adjacentelement. 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
as a parameter.
The example below uses onChange and onChangeEnd to update two separate elements with the ColorArea's value.
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.
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.
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.
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
import {ColorArea} from '@react-spectrum/color';
<ColorArea defaultValue="#7f0000" isDisabled />Custom Size
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.
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';
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
<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
In the example below, the
from a hex value, stored in state.
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.
<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.
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 andnecessityIndicator props can be used to show a required state.
<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.
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
<ColorField label="Primary Color" isQuiet />Disabled
<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.
<ColorField label="Primary Color" isReadOnly defaultValue="#e73623" />Label alignment and position
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.
<ColorField label="Primary Color" labelPosition="side" labelAlign="end" />Help text
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.
<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.
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
<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;
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
<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
In the example below, the value's type remains consistent.
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.
<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.
<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
The example below uses onChange to update a separate element with the color value as a string.
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.
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.
<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.
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.
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.
<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".
<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;
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
<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 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
so that value's type remains consistent.
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.
<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 providinglabel={null} and showValueLabel={false} to the ColorSlider, respectively. In addition, a custom label can be provided via the label prop.
<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
as a parameter.
The example below uses onChange and onChangeEnd to update two separate elements with the ColorSlider's value.
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.
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.
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.
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
<ColorSlider defaultValue="#7f0000" channel="red" isDisabled />Vertical
<ColorSlider defaultValue="#7f0000" channel="red" orientation="vertical" />Custom Size
<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.
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.
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;
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
<ColorSwatch color="#f00" />Value
ColorSwatch accepts a value via the color prop. The value should be a color string or
In the example below, the value prop of a ColorSlider, and the color prop of a ColorSwatch to show a preview of the selected color.
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.
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".
<ColorSwatch color="#f00" aria-label="Background color" colorName="Fire truck red" />Props
Visual options
Size
<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
<Flex gap="size-100">
<ColorSwatch color="#0ff" rounding="none" />
<ColorSwatch color="#0ff" rounding="default" />
<ColorSwatch color="#0ff" rounding="full" />
</Flex>Custom width
<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;
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
<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
In the example below, the value's type remains consistent.
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.
<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
The example below uses onChange to update a separate element with the color value as a string.
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
<ColorSwatchPicker size="XS">
<ColorSwatch color="#A00" />
<ColorSwatch color="#f80" />
<ColorSwatch color="#080" />
<ColorSwatch color="#08f" />
</ColorSwatchPicker>Density
<ColorSwatchPicker density="compact">
<ColorSwatch color="#A00" />
<ColorSwatch color="#f80" />
<ColorSwatch color="#080" />
<ColorSwatch color="#08f" />
</ColorSwatchPicker>Rounding
Only use rounded corners if the ColorSwatchPicker is displayed on a single row.
<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;
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
<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
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
so that value's type remains consistent.
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.
<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
as a parameter.
The example below uses onChange and onChangeEnd to update two separate elements with the ColorWheel's value.
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
<ColorWheel isDisabled />Size
<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.
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';
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
<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.
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.
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.
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.
<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.
<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><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><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.
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>
);
}Links
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.
<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
<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.
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.
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.
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.
<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
<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.
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.
/* 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.
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.
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.
<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><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
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.
<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
<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
<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
<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
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.
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.
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
<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>Menu direction
<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.
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.
// 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';
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
<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.
<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.
<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.
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.
<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.
<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.
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';
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
<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.
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
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
– 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.
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.
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.
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.
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.
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.
<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.
<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.
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.
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.
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.
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
<DateField label="Birth date" isQuiet />Disabled
<DateField label="Birth date" isDisabled />Read only
The isReadOnly boolean prop makes the DateField's value immutable. Unlike isDisabled, the DateField remains focusable.
<DateField label="Birth date" value={today(getLocalTimeZone())} isReadOnly />Label alignment and position
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.
<DateField label="Birth date" labelPosition="side" labelAlign="end" />Help text
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.
<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.
<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.
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.
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.
<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.
<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';
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
<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.
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
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
– 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.
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.
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.
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.
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.
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
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';/* 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.
<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.
<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.
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.
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.
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.
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.
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
<DatePicker label="Birth date" isQuiet />Disabled
<DatePicker label="Birth date" isDisabled />Read only
The isReadOnly boolean prop makes the DatePicker's value immutable. Unlike isDisabled, the DatePicker remains focusable.
<DatePicker label="Birth date" value={today(getLocalTimeZone())} isReadOnly />Label alignment and position
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.
<DatePicker label="Birth date" labelPosition="side" labelAlign="end" />Help text
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.
<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.
<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.
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.
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.
<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.
<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.
<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.
<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'.
<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.
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';
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
<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.
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
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
– 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.
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.
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.
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.
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.
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
import type {AnyCalendarDate} from '@internationalized/date';
import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date';/* 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.
<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.
<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.
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.
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.
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.
<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.
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.
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
<DateRangePicker label="Date range" isQuiet />Disabled
<DateRangePicker label="Date range" isDisabled />Read only
The isReadOnly boolean prop makes the DateRangePicker's value immutable. Unlike isDisabled, the DateRangePicker remains focusable.
<DateRangePicker label="Date range" value={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({weeks: 1})}} isReadOnly />Label alignment and position
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.
<DateRangePicker label="Date range" labelPosition="side" labelAlign="end" />Help text
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.
<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.
<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.
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.
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.
<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.
<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.
<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.
<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'.
<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.
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';
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
<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 onChange event, but if a value or defaultValue, values of that type will be emitted, changing only the time and preserving the date components.
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 "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
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
– 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.
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.
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".
<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".
<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.
<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.
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.
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.
<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.
<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
<TimeField label="Event time" isQuiet />Disabled
<TimeField label="Event time" isDisabled />Read only
The isReadOnly boolean prop makes the TimeField's value immutable. Unlike isDisabled, the TimeField remains focusable.
<TimeField label="Event time" value={new Time(11)} isReadOnly />Label alignment and position
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.
<TimeField label="Event time" labelPosition="side" labelAlign="end" />Help text
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.
<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.
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.
<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.
<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.
<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';
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
<DialogTrigger>
<ActionButton>Save</ActionBu