Skip to content

Chart

Visualize data.
<Chart>
<DonutChart />
<Legend>
<LegendItem />
<LegendItem />
</Legend>
</Chart>
  • Chart: Wraps and provides shared context (colors, currently hovered item, etc.) to child components. Not required if using a display-only chart without user interactions.
  • DonutChart: Displays data as parts of a whole, with an optional center value and label. Currently the only chart type available in Stylus.
  • Legend: Displays a list of labels and values related to the chart.
import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [
{ name: "Notion", amount: 281 },
{ name: "VS Code", amount: 142 },
{ name: "Slack", amount: 50 },
];
export default () => (
<Chart className="flex items-center gap-8" data={data} category="name">
<DonutChart value="amount" />
<Legend className="w-48">
{data.map((item) => (
<LegendItem
key={item.name}
dataKey={item.name}
label={item.name}
value={item.amount}
/>
))}
</Legend>
</Chart>
);

For the simplest display, you can use DonutChart standalone, without the Chart wrapper.

import { DonutChart } from "stylus-ui/Chart";
const data = [
{ name: "Notion", amount: 281 },
{ name: "VS Code", amount: 142 },
{ name: "Slack", amount: 50 },
];
export default () => (
<DonutChart category="name" value="amount" data={data} className="size-16" />
);

Each segment will automatically take on a color from the default palette. The available colors are brand, blue, green, teal, yellow, pink, fuchsia, purple, red, cyan, orange, and slate.

By default, the chart cycles through all available colors in order. To use a specific subset of colors, pass an array of names to the colors prop:

import { DonutChart } from "stylus-ui/Chart";
const data = [
{ name: "Primary", value: 40 },
{ name: "Secondary", value: 30 },
{ name: "Tertiary", value: 20 },
{ name: "Other", value: 10 },
];
export default () => (
<DonutChart
category="name"
value="value"
data={data}
colors={["brand", "pink", "cyan", "slate"]}
/>
);

Display a sum of all chart segments in the center by setting showValue to true.

import { DonutChart } from "stylus-ui/Chart";
import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [
{ name: "Notion", amount: 2814 },
{ name: "VS Code", amount: 142 },
{ name: "Slack", amount: 503 },
];
export default () => (
<DonutChart category="name" value="amount" data={data} showValue />
);

Long values will automaically shrink to fit.

import { DonutChart } from "stylus-ui/Chart";
import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [
{ name: "Notion", revenue: 4500600 },
{ name: "VS Code", revenue: 3200800 },
{ name: "Slack", revenue: 1800900 },
];
export default () => (
<DonutChart category="name" value="revenue" data={data} showValue />
);

Use the valueFormatter prop to format the display value. For example, you can use the formatIntlNumber helper to display locale-appropriate thousands separators for long numbers.

import { DonutChart } from "stylus-ui/Chart";
import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [
{ name: "Notion", amount: 2814 },
{ name: "VS Code", amount: 142 },
{ name: "Slack", amount: 503 },
];
export default () => (
<DonutChart
category="name"
value="amount"
data={data}
showValue
valueFormatter={(v) => formatIntlNumber(v)}
/>
);

To display text above the value, set label.

import { DonutChart } from "stylus-ui/Chart";
import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [
{ name: "Notion", hours: 281 },
{ name: "VS Code", hours: 142 },
{ name: "Slack", hours: 50 },
];
export default () => (
<DonutChart
category="name"
value="hours"
data={data}
showValue
valueFormatter={(v) => `${formatIntlNumber(v)} hrs`}
label="Yearly average"
/>
);

When there’s no data or all values are zero, the chart displays a full ring with a - in the center:

import { DonutChart } from "stylus-ui/Chart";
export default () => (
<DonutChart
category="name"
value="amount"
data={[]}
showValue
label="Yearly average"
/>
);

Combine the DonutChart with the Legend component for interactive data visualization. The Chart wrapper automatically synchronizes hover and click states between components with zero boilerplate:

import React from "react";
import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [
{ name: "Notion", amount: 281 },
{ name: "VS Code", amount: 142 },
{ name: "GitHub", amount: 87 },
{ name: "Slack", amount: 50 },
{ name: "Figma", amount: 43 },
];
export default () => {
const total = data.reduce((sum, item) => sum + item.amount, 0);
return (
<Chart className="flex items-center gap-8" data={data} category="name">
<DonutChart
value="amount"
label="Yearly average"
valueFormatter={(v) => `${v} hrs`}
showValue
/>
<Legend>
{data.map((item) => (
<LegendItem
key={item.name}
dataKey={item.name}
label={item.name}
value={`${item.amount} hrs (${((item.amount / total) * 100).toFixed(1)}%)`}
/>
))}
</Legend>
</Chart>
);
};

Add click handling to toggle selection by providing an onClick callback to the Chart component:

import React from "react";
import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [
{ name: "Notion", amount: 281 },
{ name: "VS Code", amount: 142 },
{ name: "GitHub", amount: 87 },
{ name: "Slack", amount: 50 },
{ name: "Figma", amount: 43 },
];
export default () => {
const [selected, setSelected] = React.useState(null);
const total = data.reduce((sum, item) => sum + item.amount, 0);
return (
<Chart
className="flex items-center gap-8"
data={data}
category="name"
selectedDataKey={selected}
onClick={setSelected}
>
<DonutChart
value="amount"
label="Yearly average"
valueFormatter={(v) => `${v} hrs`}
showValue
/>
<Legend>
{data.map((item) => (
<LegendItem
key={item.name}
dataKey={item.name}
label={item.name}
value={`${item.amount} hrs (${((item.amount / total) * 100).toFixed(1)}%)`}
/>
))}
</Legend>
</Chart>
);
};

Each legend item accepts an icon prop for FontAwesome icons or a logo prop (with name and src) to display a favicon.

import React from "react";
import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
import { getFavicon } from "stylus-ui/utils/getFavicon";
import { faGrid2 } from "@fortawesome/pro-regular-svg-icons";
const data = [
{ name: "Notion", url: "https://notion.so", amount: 281 },
{ name: "Slack", url: "https://slack.com", amount: 142 },
{ name: "Figma", url: "https://figma.com", amount: 87 },
{ name: "Others", amount: 50 },
];
export default () => {
const total = data.reduce((sum, item) => sum + item.amount, 0);
return (
<Chart
className="flex items-center gap-8"
data={data}
category="name"
colors={["brand", "pink", "cyan", "slate"]}
>
<DonutChart
value="amount"
label="Yearly average"
valueFormatter={(v) => `${v} hrs`}
showValue
/>
<Legend>
{data.map((item) => {
const logoObj = item.url && {
name: item.name,
src: getFavicon({ url: item.url }),
};
return (
<LegendItem
key={item.name}
dataKey={item.name}
logo={logoObj}
icon={!item.url ? faGrid2 : undefined}
label={item.name}
value={`${((item.amount / total) * 100).toFixed(1)}%`}
/>
);
})}
</Legend>
</Chart>
);
};

Hook to access chart interaction context. Returns null if used outside of a Chart provider. Components can use this to sync their interaction state automatically.

Chart wrapper component that provides interaction state management and color coordination for child components. Automatically synchronizes hover, click states, and colors between DonutChart, Legend, and other chart components.

children

Required
ReactNode

Child components (DonutChart, Legend, etc.)


category

string

The key in data objects to use as category names. Required when data is provided.


colors

("orange" | "yellow" | "cyan" | "blue" | "fuchsia" | "brand" | "green" | "teal" | "pink" | "purple" | "red" | "slate")[] = "Object.keys( chartColors, ) as Array<AvailableChartColorsKeys>"

Array of color keys to use for chart segments. Defaults to all available chart colors.


data

Record<string, any>[]

Array of data objects. When provided with category, enables automatic color coordination between child components.


hoveredDataKey

null | string

Controlled hover state (dataKey of the hovered item). When provided, the component operates in controlled mode.


onClick

(dataKey: string | null) => void

Callback fired when an item is clicked. Use this for controlled selection state.


onHover

(dataKey: string | null) => void

Callback fired when an item is hovered. Use this for controlled hover state.


selectedDataKey

null | string

Controlled selection state (dataKey of the selected item). When provided, the component operates in controlled mode.


A donut chart component for visualizing proportional data with an optional center label. Automatically syncs with Chart context when used inside a Chart component.

value

Required
string

The key in data objects to use as values (e.g., ‘amount’, ‘count’)


category

string

The key in data objects to use as category names (e.g., ‘name’, ‘app’). If not provided, will use category from Chart context.


colors

("orange" | "yellow" | "cyan" | "blue" | "fuchsia" | "brand" | "green" | "teal" | "pink" | "purple" | "red" | "slate")[] = "Object.keys( chartColors, ) as Array<AvailableChartColorsKeys>"

Array of color keys to use for chart segments. Defaults to all available chart colors.


data

Record<string, any>[]

Array of data objects to display in the chart. Each object should have properties matching the category and value keys. If not provided, will use data from Chart context.


label

string

Optional label text to display above the center value


labelFontSize

number = 15

Optional font size for the label text (in SVG viewBox units). Default: 15


showValue

boolean = false

Whether to hide the center value label


valueFontSize

number

Optional font size for the center value text (in SVG viewBox units).


valueFormatter

(value: number) => string = "(val: number) => val.toString()"

Function to format the center label value. Receives the total sum of all values.


A legend component for charts that displays colored indicators with labels and values. Automatically syncs with Chart context when used inside a Chart component.

children

Required
ReactNode

Child LegendItem components to display


A single item in a Legend component. Must be used as a child of Legend. Automatically syncs with Chart context when available.

dataKey

Required
string

Unique dataKey for the legend item. Used for tracking active/highlighted state and syncing with chart data.


label

Required
string

The label text to display (e.g., “Notion”, “Slack”, “Revenue”)


value

Required
string

The value text to display (e.g., “35%”, “1,234”, “$50K”)


icon

IconDefinition

Optional icon to display before the label


labelClassName

string

Optional className to apply to the label text


logo

{ name: string; src: string; }

Optional logo to display before the label


onClick

(dataKey: string | null) => void

Callback fired when clicking the legend item. Receives the item dataKey.