Chart
NewAnatomy
<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.
Usage
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>);Donut chart
Basic
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" />);Colors
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.
Specific colors only
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 value
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 />);Formatting values
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)} />);With label
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" />);No data
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" />);With legend
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> );};With click interactions
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> );};With icons and logos
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> );};