# ThemeProvider

**📖 Live documentation:** https://cds.coinbase.com/components/other/ThemeProvider/

Provides the theme context to all child components, and automatically generates CSS Variables for dynamic theming.

## Import

```tsx
import { ThemeProvider } from '@coinbase/cds-web'
```

## Examples

import { defaultTheme } from '@coinbase/cds-web/themes/defaultTheme';
import { createThemeCssVars } from '@coinbase/cds-web/core/createThemeCssVars';
import { JSONCodeBlock } from '@site/src/components/page/JSONCodeBlock';

### ThemeProvider component

The ThemeProvider provides the theme context to all child components, and automatically generates CSS Variables for dynamic theming.

You must pass the `theme` prop to configure the theme, and the `activeColorScheme` prop to set light or dark mode.

```tsx
import { ThemeProvider } from '@coinbase/cds-web/system/ThemeProvider';
import { defaultTheme } from '@coinbase/cds-web/themes/defaultTheme';

const App = () => (
  <ThemeProvider theme={defaultTheme} activeColorScheme="light">
    {/* Your app components */}
  </ThemeProvider>
);
```

:::tip
Changing the `activeColorScheme` automatically updates the values returned from the `useTheme` hook and from CSS Variables.
:::

### `useTheme` hook

The `useTheme` hook provides access to the current `theme` and `activeColorScheme`.

The `color` and `spectrum` objects automatically change based on the `activeColorScheme`.

[See the `useTheme` docs here &rarr;](/hooks/useTheme)

```jsx
const theme = useTheme();
theme.activeColorScheme; // "light" or "dark"
theme.spectrum; // Resolves to lightSpectrum or darkSpectrum, depends on activeColorScheme
theme.color; // Resolves to lightColor or darkColor, depends on activeColorScheme
theme.color.bgPrimary; // "rgb(0,82,255)" or "rgb(87,139,250)", depends on activeColorScheme
theme.space[2]; // 16
theme.borderRadius[200]; // 8
theme.fontSize.display3; // "2.5rem"
```

:::tip
For best performance, prefer to use CSS Variables instead of the `useTheme` hook whenever possible.
:::

### CSS Variables

CSS Variables are created for every value in the theme.

For best performance, prefer using CSS Variables instead of the `useTheme` hook whenever possible.

```jsx
const theme = useTheme();
theme.color.bgPrimary; // --color-bgPrimary
theme.lightColor.bgPrimary; // --lightColor-bgPrimary
theme.darkColor.bgPrimary; // --darkColor-bgPrimary
theme.spectrum.blue10; // --blue10
theme.lightSpectrum.blue10; // --light-blue10
theme.darkSpectrum.blue10; // --dark-blue10
theme.space[2]; // --space-2
theme.space[0.25]; // --space-0_25
theme.borderRadius[400]; // --borderRadius-400
theme.fontSize.body; // --fontSize-body
```

You can see all the CSS Variables for the `defaultTheme` below.

<JSONCodeBlock json={createThemeCssVars(defaultTheme)} />

#### CSS Variable inheritance

When ThemeProviders are nested, the nested provider only sets CSS variables that differ from its parent.
Unchanged values are inherited through the DOM via normal CSS custom property inheritance.

This optimization breaks when a ThemeProvider renders **outside** its parent's DOM tree — for example, inside a portal — because CSS inheritance requires DOM ancestry. In these cases, use the `isolated` prop to ensure the ThemeProvider writes all CSS variables:

```tsx
<ThemeProvider isolated theme={theme} activeColorScheme="light">
  {/* All CSS variables are written, regardless of the parent theme */}
</ThemeProvider>
```

:::tip
CDS overlay components (Modal, Toast, Alert, etc.) handle this automatically via [PortalProvider](/components/overlay/PortalProvider). You only need the `isolated` prop when rendering a ThemeProvider inside a custom portal that is not managed by CDS.
:::

### ThemeProvider classnames

The ThemeProvider renders with CSS classnames based on the `activeColorScheme` and the theme's `id`.

This allows you to style components based on the `activeColorScheme` or the theme's `id`.

```jsx
// Renders with a .cds-default class and a .light class
<ThemeProvider theme={defaultTheme} activeColorScheme="light" />
```

### Nested themes

ThemeProviders can be nested to create theme overrides for specific sections.

```jsx
<ThemeProvider theme={defaultTheme} activeColorScheme="light">
  {/* Default theme in light color scheme */}

  <ThemeProvider theme={customTheme} activeColorScheme="dark">
    {/* Custom theme in dark color scheme */}
  </ThemeProvider>
</ThemeProvider>
```

#### Overriding theme values

When nesting, you may want to override specific color values from the current theme. Overrides must be conditionally applied because we don't enforce that a theme has both light and dark colors defined.

```jsx
// Override parts of the parent theme
const theme = useTheme();
const customTheme = {
  ...theme,
  ...(theme.lightColor &&
    theme.lightSpectrum && {
      lightColor: {
        ...theme.lightColor,
        bg: `rgb(${theme.lightSpectrum.orange50})`,
        bgPrimary: `rgb(${theme.lightSpectrum.red20})`,
        bgSecondary: `rgb(${theme.lightSpectrum.blue50})`,
      },
    }),
  ...(theme.darkColor &&
    theme.darkSpectrum && {
      darkColor: {
        ...theme.darkColor,
        bg: `rgb(${theme.darkSpectrum.orange50})`,
        bgPrimary: `rgb(${theme.darkSpectrum.red20})`,
        bgSecondary: `rgb(${theme.darkSpectrum.blue50})`,
      },
    }),
} as const satisfies Theme;
```

#### Theme inheritance

Nested ThemeProviders do not automatically inherit the theme from their parent provider. You can manually inherit the theme with the `useTheme` hook.

```jsx
const Example = () => {
  // Pass the parent theme down to another ThemeProvider
  const theme = useTheme();
  return (
    <ThemeProvider theme={theme} activeColorScheme={theme.activeColorScheme}>
      {children}
    </ThemeProvider>
  );
};
```

### InvertedThemeProvider component

The InvertedThemeProvider automatically inherits from its parent theme and flips the `activeColorScheme` to the opposite value.

```jsx live
<ThemeProvider theme={defaultTheme} activeColorScheme="light">
  {/* Default theme in light color scheme */}
  <Box background="bg" padding={2} borderRadius={400}>
    <Button>Hello world!</Button>
  </Box>
  <InvertedThemeProvider>
    {/* Default theme in inverse (dark) color scheme */}
    <Box background="bg" padding={2} borderRadius={400}>
      <Button>Hello world!</Button>
    </Box>
  </InvertedThemeProvider>
</ThemeProvider>
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `activeColorScheme` | `light \| dark` | Yes | `-` | - |
| `theme` | `ThemeConfig` | Yes | `-` | - |
| `className` | `string` | No | `-` | - |
| `display` | `grid \| ruby \| table \| string & {} \| -moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none \| block \| inline \| inline-block \| flex \| inline-flex \| inline-grid \| contents \| flow-root \| list-item \| run-in \| -ms-flexbox \| -ms-grid \| -webkit-flex \| flow \| ruby-base \| ruby-base-container \| ruby-text \| ruby-text-container \| table-caption \| table-cell \| table-column \| table-column-group \| table-footer-group \| table-header-group \| table-row \| table-row-group \| -ms-inline-flexbox \| -ms-inline-grid \| -webkit-inline-flex \| inline-list-item \| inline-table` | No | `-` | - |
| `isolated` | `boolean` | No | `false` | Use the isolated prop to indicate when ThemeProvider will render outside its parents DOM tree, such as in a portal. An isolated ThemeProvider always inserts all theme CSS variables into the DOM, regardless of what overlaps with parent ThemeProvider instances. |
| `motionFeatures` | `FeatureBundle \| LazyFeatureBundle` | No | `domAnimation` | A motion feature bundle, used to selectively bundle specific framer-motion features. You likely wont need to use this this prop. |
| `style` | `CSSProperties` | No | `-` | - |


