Bar chart
Compare one measure across days and scrub any bar for its value.
chartbardata
Preview
Bar chart previewActive minutes, Sep 14–20, 2026
Live · keyboard ready
| Day | Active minutes |
|---|---|
| Mon, Sep 14 | 57 min |
| Tue, Sep 15 | 63 min |
| Wed, Sep 16 | 58 min |
| Thu, Sep 17 | 48 min |
| Fri, Sep 18 | 40 min |
| Sat, Sep 19 | 39 min |
| Sun, Sep 20 | 49 min |
When to use
- One measure across days, weeks, or months, with an average line.
- Charts with a range switch, where stable keys let bars morph between ranges.
When not to use
- Use sparkline for a compact trend.
- Use activity-heatmap for daily rhythm over a year.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "bar-chart",
"type": "registry:ui",
"title": "Bar chart",
"description": "Compare one measure across days and scrub any bar for its value.",
"categories": [
"data"
],
"dependencies": [
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/bar-chart/bar-chart.tsx",
"type": "registry:component",
"target": "@components/manicat/bar-chart/bar-chart.tsx"
},
{
"path": "registry/components/bar-chart/bar-chart.module.css",
"type": "registry:component",
"target": "@components/manicat/bar-chart/bar-chart.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/bar-chart",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/bar-chart",
"markdown": "https://uiarc.dev/components/bar-chart/markdown",
"tags": [
"chart",
"bar",
"data"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { BarChart } from "@/components/manicat/bar-chart/bar-chart";
import type { BarChartDatum } from "@/components/manicat/bar-chart/bar-chart";
const DAY = 86_400_000;
function iso(date: Date): string {
return date.toISOString().slice(0, 10);
}
function dayLabel(date: Date): string {
return date.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", timeZone: "UTC" });
}
function range(from: Date, days: number): Date[] {
return Array.from({ length: days }, (_, index) => new Date(from.getTime() + index * DAY));
}
const barData: BarChartDatum[] = range(new Date(Date.UTC(2026, 8, 14, 12)), 7).map((date, index) => ({
key: iso(date),
label: dayLabel(date),
axisLabel: date.toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" }),
value: 42 + Math.round(18 * Math.sin(index / 1.3 + 1) + index * 3),
}));
export function BarChartExample() {
return <BarChart data={barData} label="Active minutes" period="Sep 14–20, 2026" unit="min" showAverage />;
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Weekly activity A week of daily values with the average drawn as a reference line.
Weekly activity
A week of daily values with the average drawn as a reference line. previewActive minutes, Sep 14–20, 2026
Live · keyboard ready
| Day | Active minutes |
|---|---|
| Monday, Sep 14 | 38 min |
| Tuesday, Sep 15 | 52 min |
| Wednesday, Sep 16 | 47 min |
| Thursday, Sep 17 | 61 min |
| Friday, Sep 18 | 55 min |
| Saturday, Sep 19 | 24 min |
| Sunday, Sep 20 | 19 min |
weekly-activity-a-week-of-daily-values-with-the-average-drawn-as-a-reference-line-.tsxtsx
"use client";
import { BarChart } from "@/components/manicat/bar-chart/bar-chart";
import type { BarChartDatum } from "@/components/manicat/bar-chart/bar-chart";
const DAY = 86_400_000;
const activeMinutes: BarChartDatum[] = Array.from({ length: 7 }, (_, index) => {
const date = new Date(Date.UTC(2026, 8, 14, 12) + index * DAY);
return {
key: date.toISOString().slice(0, 10),
label: date.toLocaleDateString("en-US", { weekday: "long", month: "short", day: "numeric", timeZone: "UTC" }),
axisLabel: date.toLocaleDateString("en-US", { weekday: "short", timeZone: "UTC" }),
value: [38, 52, 47, 61, 55, 24, 19][index],
};
});
export function BarChartWeeklyExample() {
return <BarChart data={activeMinutes} label="Active minutes" period="Sep 14–20, 2026" unit="min" showAverage />;
}
Keyboard interactions
| Keys | Action |
|---|---|
| ArrowLeft / ArrowRight | Moves between bars (ArrowDown and ArrowUp too). |
| PageDown / PageUp | Moves up to seven bars. |
| Home / End | Jumps to the first or last bar. |
| Escape | Stops scrubbing and returns to the average. |
Accessibility
- The SVG is role="img" with a summary of average, highest, and lowest.
- A focusable role="slider" scrubber reads each bar as its label and value.
- A visually hidden table lists every value, and range changes are announced politely.
Motion
- Bars grow in with a stagger on first view; new ranges morph bars, ticks, and the average line.
- Scrubbing moves a cursor and rolls the headline value.
- Reduced motion shows bars at once and swaps values without movement.
Responsive behavior
- The plot measures its width with a ResizeObserver and bars cap at 28px, so wide containers get spacing rather than fat bars.
- Scrubbing uses touch-action pan-y, so a horizontal drag scrubs while vertical swipes scroll the page.
Performance
- Bars, ticks, and the average line animate with motion values; one series and many bars per view is the intended load.
- A visually hidden table renders every value, so very long ranges double the DOM.
Notes for AI
- Use for one measure across days, weeks, or months. Use sparkline for a compact trend and activity-heatmap for daily rhythm over a year.
- Use stable keys such as ISO dates so switching ranges morphs instead of redrawing.