Spectrum 2 Overview
Spectrum 2 support in React Spectrum Charts is actively under development. The S2 package still has some Spectrum 1 dependencies that are being incrementally migrated. We will make an effort not to introduce breaking API changes to Spectrum 2 features prior to Adobe Summit.
React Spectrum Charts offers two paths for Spectrum 2 support. This page explains the difference between them, how to install the full S2 package, and how theming works.
S2 prop vs. the S2 package
s2 prop (partial S2 support)
The @adobe/react-spectrum-charts package includes an s2 prop on the Chart component. When set to true, it enables Spectrum 2 styling for supported chart types. Currently supported: Line, Bar, and Donut.
import { Chart, Bar } from '@adobe/react-spectrum-charts';
<Chart data={data} s2>
<Bar color="series" />
</Chart>
Use this approach if you are already using @adobe/react-spectrum-charts and only need S2 theming for supported chart types.
@spectrum-charts/react-spectrum-charts-s2 package (full S2 support)
The S2 package is a separate alpha package built entirely on Spectrum 2. It provides a full S2-native implementation of the chart library with additional features not available in the base package. All components are imported from this package instead of @adobe/react-spectrum-charts.
Use this approach when you need access to S2-exclusive features such as line gradients, line labels, line interpolation, or S2 reference lines.
Installing the S2 package
The S2 package is published under the alpha tag on npm.
npm install @spectrum-charts/react-spectrum-charts-s2
# or
yarn add @spectrum-charts/react-spectrum-charts-s2
Import components from the S2 package instead of the base package:
import { Chart, Axis, Line, Legend } from '@spectrum-charts/react-spectrum-charts-s2';
The S2 package has these peer dependencies, which your app must install:
| Package | Version |
|---|---|
@react-spectrum/s2 | >=1.0.0 |
react, react-dom | >=19.0.0 |
react-aria-components | ^1.14.0, matching the version your @react-spectrum/s2 depends on |
vega | ^6.2.0 |
vega-lite | ^6.4.2 |
Since 1.3.0, @react-spectrum/s2 pins an exact react-aria-components version. Install that same version so your app has one copy and charts share React Aria context with S2:
npm install @react-spectrum/s2 react react-dom vega vega-lite
npm ls react-aria-components # shows the version @react-spectrum/s2 uses
npm install react-aria-components@<that version>
Module format
The S2 packages ship ES modules only, one file per module, so bundlers include only what you import. require() and the UMD global are not supported, and Node.js 20.19 or later is required.
Jest
Jest runs CommonJS, so it must transform the package and its ES module dependencies (Vega and d3), and stub CSS. Works with Jest 29 and 30:
// jest.config.js — transforms use your babel.config.js; a .babelrc does not apply to node_modules.
module.exports = {
testEnvironment: './jest.environment.js',
transformIgnorePatterns: [
'node_modules/(?!(@spectrum-charts|vega|vega-[^/]+|d3-[^/]+|internmap|delaunator|robust-predicates|json-stringify-pretty-compact)/)',
],
moduleNameMapper: {
'\\.css$': '<rootDir>/test/styleStub.js',
// Jest 30 otherwise loads vega-canvas's Node build, which uses top-level await.
'^vega-canvas$': '<rootDir>/node_modules/vega-canvas/build/vega-canvas.browser.js',
},
};
// jest.environment.js — jsdom does not provide structuredClone, which Vega-Lite uses.
const { TestEnvironment } = require('jest-environment-jsdom');
module.exports = class extends TestEnvironment {
async setup() {
await super.setup();
this.global.structuredClone = structuredClone;
}
};
// test/styleStub.js
module.exports = {};
Vitest
Vitest runs ES modules natively. Inline this package and @react-spectrum/s2 so Vite handles their CSS imports:
// vitest.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
server: {
deps: { inline: ['@spectrum-charts/react-spectrum-charts-s2', '@react-spectrum/s2'] },
},
},
});
Theming
Color scheme
The Chart component accepts a colorScheme prop to switch between light and dark mode. This controls both the chart's visual theme and the styling of tooltips and popovers.
<Chart data={data} colorScheme="dark">
<Line color="series" />
</Chart>
| Value | Description |
|---|---|
'light' | Light mode (default) |
'dark' | Dark mode |
Colors
The S2 package uses Spectrum 2 categorical color scales by default. You can override the color scale using the colors prop on Chart.
Available S2 color scales: s2Categorical6, s2Categorical12, s2Categorical16, s2Categorical20.
<Chart data={data} colors="s2Categorical12">
<Line color="series" />
</Chart>
Inspect panels
Inspect panels in the S2 package are styled automatically to match the Spectrum 2 design specification — including the correct font (adobe-clean), text colors, border, and elevated box shadow. No additional configuration is required.
Add a ChartInspect as a child of a mark component to enable inspect panels:
<Line color="series">
<ChartInspect>
{(datum) => (
<div>
<div>Series: {datum.series}</div>
<div>Value: {datum.value}</div>
</div>
)}
</ChartInspect>
</Line>
Popovers
Popovers are styled to match the Spectrum 2 elevated surface style (border, box shadow, font). Add a ChartPopover as a child of a mark component to enable click-to-open popovers:
<Line color="series">
<ChartPopover width={200}>
{(datum, close) => (
<div>
<div>Series: {datum.series}</div>
<div>Value: {datum.value}</div>
<button onClick={close}>Close</button>
</div>
)}
</ChartPopover>
</Line>
ChartInspect props
| name | type | default | description |
|---|---|---|---|
| children | (datum: Datum) => ReactNode | – | Callback that returns the content to render inside the inspect panel. Optional inside a Donut, which renders default content when omitted. |
| excludeDataKeys | string[] | – | Keys in the data that, if they have truthy values, will suppress the inspect panel for that data point. |
| highlightBy | 'series' | 'dimension' | 'item' | string[] | 'item' | Controls which marks are highlighted when an inspect panel is visible. |
| targets | ('dimensionArea' | 'item')[] | ['item'] | The hit targets that trigger the inspect panel. |
ChartPopover props
| name | type | default | description |
|---|---|---|---|
| children | (datum: Datum, close: () => void) => ReactNode | – | Callback that returns the content to render inside the popover. The second argument is a function that closes the popover. Optional inside a Donut, which renders default content when omitted. |
| width | number | 'auto' | 250 | Width of the popover in pixels, or 'auto' to size to content. |
| minWidth | number | 0 | Minimum width of the popover in pixels. |
| maxWidth | number | – | Maximum width of the popover in pixels. |
| height | number | 'auto' | – | Height of the popover in pixels. |
| minHeight | number | – | Minimum height of the popover in pixels. |
| maxHeight | number | – | Maximum height of the popover in pixels. |
| contentMargin | number | 12 | Inner margin applied around the popover content in pixels. |
| onOpenChange | (isOpen: boolean) => void | – | Callback fired when the popover opens or closes. |
| rightClick | boolean | false | When true, the popover opens on right-click instead of left-click. |
| containerPadding | number | 12 | Minimum distance between the popover and the edges of its container. |