{/* Copyright 2023 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 ChevronRight from '@spectrum-icons/workflow/ChevronRight';

import {Layout} from '@react-spectrum/docs';
export default Layout;

---
category: Guides
keywords: [aria, accessibility, react, component]
description: React Aria Components do not include any styles by default, allowing you to build custom designs to fit your application or design system using any styling solution.
order: 0
---

# Styling

React Aria Components do not include any styles by default, allowing you to build custom designs to fit your application or design system using any styling solution.

## Class names

Each component accepts the standard `className` and `style` props which enable using vanilla CSS, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc.

When a custom `className` is not provided, each component includes a default class name following the `react-aria-ComponentName` naming convention. You can use this to style a component with standard CSS without needing any custom classes.

```css render=false
.react-aria-Select {
  /* ... */
}
```

A custom `className` can also be specified on any component. This overrides the default `className` provided by React Aria with your own.

```jsx
<Select className="my-select">
  {/* ... */}
</Select>
```

The default class names for each component are listed in the Styling section of their documentation.

## States

Components often support multiple UI states (e.g. pressed, hovered, selected, etc.). React Aria Components exposes states using data attributes, which you can target in CSS selectors. They can be thought of like custom CSS pseudo classes. For example:

```css render=false
.react-aria-ListBoxItem[data-selected] {
  /* ... */
}

.react-aria-ListBoxItem[data-focused] {
  /* ... */
}
```

In order to ensure high quality interactions across browsers and devices, React Aria Components includes states such as `data-hovered` and `data-pressed` which are similar to CSS pseudo classes such as `:hover` and `:active`, but work consistently between mouse, touch, and keyboard modalities. You can read more about this in our [blog post series](../blog/building-a-button-part-1.html) and our [Interactions](interactions.html) overview.

All states supported by each component are listed in the Styling section of their documentation.

## Render props

The `className` and `style` props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply.

```jsx
<ListBoxItem className={({isSelected}) => isSelected ? 'selected' : 'unselected'}>
  Item
</ListBoxItem>
```

Render props may also be used as children to alter what elements are rendered based on the current state. For example, you could render a checkmark icon when an item is selected.

```jsx
<ListBoxItem>
  {({isSelected}) => (
    <>
      {isSelected && <CheckmarkIcon />}
      <span>Item</span>
    </>
  )}
</ListBoxItem>
```

Render props also let you modify the default values provided by React Aria via the `defaultClassName`, `defaultStyle`, and `defaultChildren` options. For example, you could wrap the default children of a `SelectValue` in an extra element, append an additional class name to React Aria's default, or merge default inline styles with your own.

```jsx
<SelectValue>
  {({defaultChildren}) => <span>{defaultChildren}</span>}
</SelectValue>
```

The render props exposed for each component are listed in the Styling section of their documentation.

## Slots

Some components include multiple instances of the same component as children. These use the `slot` prop to distinguish them, which can also be used in CSS for styling purposes. This example targets the increment and decrement buttons in a [NumberField](NumberField.html).

```tsx
<NumberField>
  <Label>Width</Label>
  <Group>
    <Input />
    {/*- begin highlight -*/}
    <Button slot="increment">+</Button>
    <Button slot="decrement">-</Button>
    {/*- end highlight -*/}
  </Group>
</NumberField>
```

```css render=false
.react-aria-NumberField {
  /*- begin highlight -*/
  [slot=increment] { /*- end highlight -*/
    border-radius: 4px 4px 0 0;
  }

  /*- begin highlight -*/
  [slot=decrement] { /*- end highlight -*/
    border-radius: 0 0 4px 4px;
  }
}
```

The slots supported by each component are shown in the Anatomy section of their documentation.

## CSS variables

Some components provide CSS variables that you can use in your styling code. For example, the [Select](Select.html) component provides a `--trigger-width` variable on the popover that is set to the width of the trigger button. You can use this to make the width of the popover match the width of the button.

```css render=false
.react-aria-Popover {
  width: var(--trigger-width);
}
```

The CSS variables provided by each component are listed in the Styling section of their documentation.

## Tailwind CSS

[Tailwind CSS](https://tailwindcss.com) is a utility-first CSS framework for rapid styling that works great with React Aria Components. To access [states](#states), you can use [data attributes](https://tailwindcss.com/docs/hover-focus-and-other-states#data-attributes) as modifiers:

```jsx
<ListBoxItem className="data-[selected]:bg-blue-400 data-[disabled]:bg-gray-100">
  Item
</ListBoxItem>
```

Alternatively, you can use [render props](#render-props) to control which Tailwind classes are applied based on states. This can be useful if you need to apply multiple classes based on a single state:

```jsx
<Radio
  className={({isFocusVisible, isSelected}) => `
    flex rounded-lg p-4
    ${isFocusVisible ? 'ring-2 ring-blue-600 ring-offset-1' : ''}
    ${isSelected ? 'bg-blue-600 border-white/30 text-white' : ''}
  `}>
  {/* ... */}
</Radio>
```

To access [CSS variables](#css-variables), use Tailwind's [arbitrary value](https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values) syntax.

```jsx
<Popover className="w-[--trigger-width]">
  {/* ... */}
</Popover>
```

### Plugin

A Tailwind CSS plugin is also available to make styling states of React Aria Components easier, with shorter names and autocomplete in your editor. To install:

```
yarn add tailwindcss-react-aria-components
```

When using Tailwind v4, add it to your CSS:

```css render=false
@import "tailwindcss";
@plugin "tailwindcss-react-aria-components";
```

<details>

<summary style={{fontWeight: 'bold', paddingBottom: 8}}><ChevronRight size="S" /> Tailwind v3</summary>

When using Tailwind v3, install `tailwindcss-react-aria-components` version 1.x instead of 2.x, and add the plugin to your `tailwind.config.js` instead:

```tsx
/** @type {import('tailwindcss').Config} */
module.exports = {
  // ...
  plugins: [
    require('tailwindcss-react-aria-components')
  ]
};
```

</details>

With the plugin installed, you can now access all states without the `data-` prefix. If you have the [Tailwind VSCode Extension](https://tailwindcss.com/docs/editor-setup#intelli-sense-for-vs-code) installed, you'll also get autocomplete for all states in your editor.

```jsx
<ListBoxItem className="selected:bg-blue-400 disabled:bg-gray-100">
  Item
</ListBoxItem>
```

### Boolean states

Boolean states such as `data-pressed` can be styled with `pressed:` like this:

```jsx
<Button className="pressed:bg-blue">
  {/* ... */}
</Button>
```

### Non-boolean states

Non-boolean states follow the `{name}-{value}` pattern, so you can style an element with `data-orientation="vertical"` using `orientation-vertical:`.

```jsx
<Tabs className="orientation-vertical:flex-row">
  {/* ... */}
</Tabs>
```

### Modifier prefix

By default, all modifiers are unprefixed (e.g. `disabled:`), and generate CSS that automatically handles both React Aria Components and native CSS pseudo classes when the names conflict. If you prefer, you can optionally prefix all React Aria Components modifiers with a string of your choice.

```css render=false
@plugin "tailwindcss-react-aria-components" { prefix: rac };
```

<details>

<summary style={{fontWeight: 'bold', paddingBottom: 8}}><ChevronRight size="S" /> Tailwind v3</summary>

When using Tailwind v3, pass the prefix option to the plugin in `tailwind.config.js`:

```jsx
/** @type {import('tailwindcss').Config} */
module.exports = {
  plugins: [
    require('tailwindcss-react-aria-components')({prefix: 'rac'})
  ],
};
```

</details>

With this configured, all states for React Aria Components can be accessed with that prefix.

```jsx
<ListBoxItem className="rac-selected:bg-blue-400 rac-disabled:bg-gray-100">
  Item
</ListBoxItem>
```

## Animation

React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Motion](https://motion.dev/).

### CSS transitions

Overlay components such as [Popover](Popover.html) and [Modal](Modal.html) support entry and exit animations via the `[data-entering]` and `[data-exiting]` states, or via the corresponding render prop functions.

* `[data-entering]` represents the starting state of the entry animation. The component will transition from the entering state to the default state when it opens.
* `[data-exiting]` represents the ending state of the exit animation. The component will transition from the default state to the exiting state and wait for any animations to complete before being removed from the DOM.

```css render=false
.react-aria-Popover {
  transition: opacity 300ms;

  &[data-entering],
  &[data-exiting] {
    opacity: 0;
  }
}
```

Note that the `[data-entering]` state is only applied for one frame when using CSS transitions. The transition itself should be assigned in the default state. To create a different exit animation, assign the transition in the `[data-exiting]` state.

```css render=false
.react-aria-Popover {
  /* entry transition */
  transition: transform 300ms, opacity 300ms;

  /* starting state of the entry transition */
  &[data-entering] {
    opacity: 0;
    transform: scale(0.8);
  }

  &[data-exiting] {
    /* exit transition */
    transition: opacity 150ms;
    /* ending state of the exit transition */
    opacity: 0;
  }
}
```

### CSS animations

For more complex animations, you can also apply CSS keyframe animations using the same `[data-entering]` and `[data-exiting]` states.

```css render=false
.react-aria-Popover[data-entering] {
  animation: slide 300ms;
}

.react-aria-Popover[data-exiting] {
  animation: slide 300ms reverse;
}

@keyframes slide {
  from {
    transform: translateY(-20px);
    opacity: 0;
  }

  to {
    transform: translateY(0);
    opacity: 1;
  }
}
```

Note that unlike CSS transitions, keyframe animations are not interruptible. If the user opens and closes an overlay quickly, the animation may appear to jump to the ending state before the next animation starts.

### Tailwind CSS

If you are using Tailwind CSS, we recommend using the [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate) plugin. This includes utilities for building common animations such as fading, sliding, and zooming.

```jsx
<Popover className="data-[entering]:animate-in data-[entering]:fade-in data-[exiting]:animate-out data-[exiting]:fade-out">
  {/* ... */}
</Popover>
```

### Motion

[Motion](https://motion.dev) and other JavaScript animation libraries can also be used with React Aria Components. Use the [motion](https://motion.dev/docs/react-motion-component#custom-components) function to create a wrapper component that adds support for Motion's animation props.

```tsx
import {Modal, ModalOverlay} from 'react-aria-components';
import {motion} from 'motion/react';

// Create a Motion wrappers.
const MotionModal = motion(Modal);
const MotionModalOverlay = motion(ModalOverlay);
```

This enables using props like [animate](https://motion.dev/docs/react-animation) with React Aria Components.

```tsx
<MotionModal
  initial={{opacity: 0}}
  animate={{opacity: 1}}>
  {/* ... */}
</MotionModal>
```

Overlay exit animations can be implemented using the `isExiting` prop, which keeps the element in the DOM until an animation is complete. Motion's [variants](https://motion.dev/docs/react-animation#variants) are a good way to setup named animation states.

```tsx
type AnimationState = 'unmounted' | 'hidden' | 'visible';

function Example() {
  /*- begin highlight -*/
  // Track animation state.
  let [animation, setAnimation] = React.useState<AnimationState>('unmounted');
  /*- end highlight -*/

  return (
    <DialogTrigger
      /*- begin highlight -*/
      // Start animation when open state changes.
      onOpenChange={isOpen => setAnimation(isOpen ? 'visible' : 'hidden')}
      /*- end highlight -*/
    >
      <Button>Open dialog</Button>
      <MotionModalOverlay
        /*- begin highlight -*/
        // Prevent modal from unmounting during animation.
        isExiting={animation === 'hidden'}
        // Reset animation state once it is complete.
        onAnimationComplete={animation => {
          setAnimation(a => animation === 'hidden' && a === 'hidden' ? 'unmounted' : a)
        }}
        /*- end highlight -*/
        variants={{
          hidden: {opacity: 0},
          visible: {opacity: 1}
        }}
        initial="hidden"
        animate={animation}>
        <MotionModal
          variants={{
            hidden: {opacity: 0, y: 32},
            visible: {opacity: 1, y: 0}
          }}>
          {/* ... */}
        </MotionModal>
      </MotionModalOverlay>
    </DialogTrigger>
  );
}
```

**Note**: Motion's `AnimatePresence` component may not work with React Aria overlays in all cases, so the example shown above is the recommended approach for exit animations.

The [AnimatePresence](https://motion.dev/docs/react-animate-presence) component allows you to animate when items are added or removed in collection components. Use `array.map` to create children, and make sure each child has a unique `key` in addition to an `id` to ensure Motion can track it.

```tsx
import {GridList, GridListItem} from 'react-aria-components';
import {motion, AnimatePresence} from 'motion/react';

const MotionItem = motion(GridListItem);

<GridList>
  <AnimatePresence>
    {items.map(item => (
      <MotionItem
        key={item.id}
        id={item.id}
        layout
        exit={{opacity: 0}}>
        {/* ... */}
      </MotionItem>
    ))}
  </AnimatePresence>
</GridList>
```
