App provider
A top level provider for the Design System.Beta
New and ready to use. We'll provide comms and support for any major changes.
Theming
App provider sets up theming for an app, enabling design tokens to be used.
Theming with design tokens
import React from 'react';
import { Box } from '@atlaskit/primitives/compiled';
import AppProvider from '@atlaskit/app-provider';
function ThemedComponent() {
return (
<Box backgroundColor="elevation.surface" padding="space.200">
<Box as="h3">
Theming with design tokens
</Box>
</Box>
);
}
function AppProviderTheme() {
return (
<AppProvider>
<ThemedComponent />
</AppProvider>
);
}Color mode and theme switching
The useColorMode hook can be used to get the active color mode. When the color mode is set to
auto, the active color mode will be determined by the user's system preference.
The useTheme hook can be used to get the active themes. The useSetTheme hook can be used to
change themes.
Current color mode: light
Current light theme: light
import React from 'react';
import { Box } from '@atlaskit/primitives/compiled';
import AppProvider from '@atlaskit/app-provider';
function ColorModeSwitcher() {
const colorMode = useColorMode();
const setColorMode = useSetColorMode();
return (
<Box backgroundColor="elevation.surface" padding="space.200">
<Box as="h3" paddingBlockEnd="space.200">
Current color mode: {colorMode}
</Box>
<DropdownMenu trigger="Change color mode">
<DropdownItemGroup>
<DropdownItem onClick={() => setColorMode('light')}>
Light
</DropdownItem>
<DropdownItem onClick={() => setColorMode('dark')}>Dark</DropdownItem>
<DropdownItem onClick={() => setColorMode('auto')}>Auto</DropdownItem>
</DropdownItemGroup>
</DropdownMenu>
</Box>
);
}
function ThemeSwitcher() {
const theme = useTheme();
const setTheme = useSetTheme();
return (
<Box backgroundColor="elevation.surface" padding="space.200">
<Box as="h3" paddingBlockEnd="space.200">
Current light theme: {theme.light}
</Box>
<DropdownMenu trigger="Change light theme">
<DropdownItemGroup>
<DropdownItem onClick={() => setTheme({ light: 'light' })}>
Light theme
</DropdownItem>
</DropdownItemGroup>
</DropdownMenu>
</Box>
);
}
function AppProviderTheme() {
return (
<AppProvider defaultColorMode="auto">
<ColorModeSwitcher />
<ThemeSwitcher />
</AppProvider>
);
}Sub-tree theming
Use sub-tree theming to apply a different color mode or theme to a specific section of your UI, independently of the rest of the page. This is useful for content that needs a distinct visual treatment.
Nest ThemeProvider inside AppProvider to scope a theme to part of the page.
This sub-tree theme is light mode.
This nested sub-tree theme is dark mode.
import React from 'react';
import AppProvider, { ThemeProvider } from '@atlaskit/app-provider';
import Button from '@atlaskit/button/new';
import { Box, Inline, Stack, Text } from '@atlaskit/primitives/compiled';
function SubTreeThemingExample() {
return (
<AppProvider>
<ThemeProvider defaultColorMode="light">
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">This sub-tree theme is light mode.</Text>
<Inline space="space.100">
<Button>Button</Button>
</Inline>
<ThemeProvider defaultColorMode="dark">
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">This nested sub-tree theme is dark mode.</Text>
<Inline space="space.100">
<Button>Button</Button>
</Inline>
</Stack>
</Box>
</ThemeProvider>
</Stack>
</Box>
</ThemeProvider>
</AppProvider>
);
}Portalled content
Components that render into a portal render outside the ThemeProvider's DOM subtree. By default,
portalled content inherits the root theme rather than the sub-tree theme.
To support sub-tree theming for portalled content, use the Portal component, which passes the color mode to the content it renders.
Sub-tree theming outside AppProvider
If AppProvider has not yet been adopted in your application, ThemeProvider can still be used
standalone to apply scoped theming. In this case, every ThemeProvider including the outermost one
behaves as a sub-tree theme. It wraps its children in a scoped div and does not set page-level
theme attributes on html or body.
This sub-tree theme is light mode.
This nested sub-tree theme is dark mode.
/**
* When used outside of AppProvider, ThemeProvider scopes its theme to its
* own subtree only. It does not set page-level theme attributes, so the
* page background and other global styles will not be themed.
*
* For full page theming, use AppProvider at the root of your application.
*/
import React from 'react';
import { ThemeProvider } from '@atlaskit/app-provider/theme-provider';
import Button from '@atlaskit/button/default/button';
import { Box, Inline, Stack, Text } from '@atlaskit/primitives/compiled';
function SubTreeThemingOutsideAppProviderExample(): React.JSX.Element {
return (
<ThemeProvider defaultColorMode="light">
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">This sub-tree theme is light mode.</Text>
<Inline space="space.100">
<Button>Button</Button>
</Inline>
<ThemeProvider defaultColorMode="dark">
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">This nested sub-tree theme is dark mode.</Text>
<Inline space="space.100">
<Button>Button</Button>
</Inline>
</Stack>
</Box>
</ThemeProvider>
</Stack>
</Box>
</ThemeProvider>
);
}
export default SubTreeThemingOutsideAppProviderExample;Dynamic color mode inversion
A common pattern is to create a panel that always displays the opposite color mode to its surroundings. For example, a dark sidebar in a light-mode app, or a highlighted callout that always contrasts with its parent.
To do this reliably, read the parent color mode with useColorMode(), then call useSetColorMode()
inside the sub-tree ThemeProvider to update it reactively.
Avoid relying on defaultColorMode alone for this pattern. The prop is only applied on initial
mount and does not react to subsequent changes in the parent's color mode.
Parent panel.
This panel is always the opposite color mode to its parent. It reacts to changes in the parent's color mode automatically.
import React, { useEffect } from 'react';
import AppProvider, { ThemeProvider, useColorMode, useSetColorMode } from '@atlaskit/app-provider';
import Button from '@atlaskit/button/new';
import { Box, Inline, Stack, Text } from '@atlaskit/primitives/compiled';
import type { ThemeColorModes } from '@atlaskit/tokens';
function InvertedPanelContent({ parentColorMode }: { parentColorMode: ThemeColorModes }) {
const setColorMode = useSetColorMode();
useEffect(() => {
// Always apply the opposite of the parent color mode
setColorMode(parentColorMode === 'light' ? 'dark' : 'light');
}, [parentColorMode, setColorMode]);
return (
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">
This panel is always the opposite color mode to its parent.
It reacts to changes in the parent's color mode automatically.
</Text>
<Inline space="space.100">
<Button>Inverted button</Button>
</Inline>
</Stack>
</Box>
);
}
function InvertedPanel() {
// Read the color mode from the nearest parent ThemeProvider (or AppProvider)
const parentColorMode = useColorMode();
return (
<ThemeProvider>
<InvertedPanelContent parentColorMode={parentColorMode} />
</ThemeProvider>
);
}
function DynamicSubTreeThemingExample() {
return (
<AppProvider defaultColorMode="light">
<Box backgroundColor="elevation.surface" padding="space.300">
<Stack space="space.200">
<Text as="p">Parent panel.</Text>
<InvertedPanel />
</Stack>
</Box>
</AppProvider>
);
}Router links
The routerLinkComponent prop provides support for configuring router links within Design System
components.
Support for this is limited as Design System components are being updated, starting with:
This example shows how links can be configured to utilize the router link component supplied by React Resource Router, (opens new window), however any routing library can be used.
import React, { forwardRef, type Ref } from 'react';
import {
Link,
type LinkProps,
RouteComponent,
Router,
} from 'react-resource-router';
import AppProvider, { type RouterLinkComponentProps } from '@atlaskit/app-provider';
type ReactResourceRouterLinkConfig = Pick<LinkProps, 'to' | 'href' | 'replace'>;
const MyRouterLinkComponent = forwardRef(
(
{
href,
children,
...rest
}: RouterLinkComponentProps<ReactResourceRouterLinkConfig>,
ref: Ref<HTMLAnchorElement>,
) => {
// A basic link by passing a string as the component's `href` prop.
if (typeof href === 'string') {
return (
<Link ref={ref} href={href} {...rest}>
{children}
</Link>
);
}
// Advanced link configuration by passing an object as the
// component's `href` prop
return (
<Link
ref={ref}
href={href.href}
to={href.to}
replace={href.replace}
{...rest}
>
{children}
</Link>
);
},
);
const routes = [
{
name: 'home',
path: '',
exact: true,
component: () => <div>Home page component</div>,
},
];
function App() {
return (
<AppProvider routerLinkComponent={MyRouterLinkComponent}>
<Router routes={routes}>
<RouteComponent />
</Router>
</AppProvider>
);
}