Skip to content
  1. Keyline UI
  2. Chart
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Chart

Charts are Recharts; Keyline adds the construction: a hairline grid, code-voice ticks, square marks and an inverse tooltip. Series take colours by role, focal first, then comparison. ChartFigure adds a Table view of the same rows, so no reading depends on colour or on seeing the plot.

Loading the live example…

"use client"

import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartFigure, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/keyline/ui/chart"

const SALES = [
  { rep: "Dwight", q4: 5_120, q3: 4_760 },
  { rep: "Jim", q4: 4_380, q3: 4_510 },
  { rep: "Phyllis", q4: 3_640, q3: 3_200 },
  { rep: "Stanley", q4: 3_410, q3: 3_390 },
]

// the first series takes the focal colour, the second the comparison colour
const config = {
  q4: { label: "Q4", unit: "reams" },
  q3: { label: "Q3", unit: "reams" },
} satisfies ChartConfig

export function SalesByRep() {
  return (
    <ChartFigure title="Paper sold by rep" description="Scranton, Q4 against Q3" data={SALES} config={config} labelKey="rep" labelHeader="Rep">
      <ChartContainer config={config} className="aspect-auto h-[260px]">
        <BarChart data={SALES} margin={{ top: 8, right: 0, bottom: 0, left: 0 }} barGap={2} barCategoryGap="24%">
          <CartesianGrid vertical={false} />
          <XAxis dataKey="rep" tickLine={false} tickMargin={8} />
          <YAxis tickLine={false} axisLine={false} width={36} />
          <ChartTooltip content={<ChartTooltipContent />} />
          <ChartLegend content={<ChartLegendContent />} />
          <Bar dataKey="q4" fill="var(--color-q4)" />
          <Bar dataKey="q3" fill="var(--color-q3)" />
        </BarChart>
      </ChartContainer>
    </ChartFigure>
  )
}

Installation

npx shadcn@latest add @keyline-ui/chart

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartFigure, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/keyline/ui/chart"
const config = {
  q4: { label: "Q4", unit: "reams" },
  q3: { label: "Q3", unit: "reams" },
} satisfies ChartConfig

<ChartFigure title="Paper sold by rep" data={sales} config={config} labelKey="rep" labelHeader="Rep">
  <ChartContainer config={config}>
    <BarChart data={sales}>
      <CartesianGrid vertical={false} />
      <XAxis dataKey="rep" tickLine={false} />
      <YAxis tickLine={false} axisLine={false} />
      <ChartTooltip content={<ChartTooltipContent />} />
      <Bar dataKey="q4" fill="var(--color-q4)" />
      <Bar dataKey="q3" fill="var(--color-q3)" />
    </BarChart>
  </ChartContainer>
</ChartFigure>

Examples

A line against a target

A ReferenceLine marks the target. Tab to the plot and use the arrow keys to step through the weeks; each one is read out.

Loading the live example…

"use client"

import { CartesianGrid, Line, LineChart, ReferenceLine, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartFigure, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/keyline/ui/chart"

const WEEKS = [
  { week: "W40", reams: 1_620 },
  { week: "W41", reams: 1_680 },
  { week: "W42", reams: 1_540 },
  { week: "W43", reams: 1_880 },
  { week: "W44", reams: 1_760 },
  { week: "W45", reams: 1_810 },
]

const config = { reams: { label: "Reams sold", unit: "reams" } } satisfies ChartConfig

export function WeeklyTarget() {
  return (
    <ChartFigure title="Reams sold each week" description="Corporate's target is 1,700" data={WEEKS} config={config} labelKey="week" labelHeader="Week">
      <ChartContainer config={config} className="aspect-auto h-[240px]">
        <LineChart data={WEEKS} margin={{ top: 8, right: 12, bottom: 0, left: 0 }}>
          <CartesianGrid vertical={false} />
          <XAxis dataKey="week" tickLine={false} tickMargin={8} />
          <YAxis tickLine={false} axisLine={false} width={44} domain={[1_200, 2_000]} />
          <ReferenceLine y={1_700} label={{ value: "Target 1,700", position: "insideBottomLeft" }} />
          <ChartTooltip content={<ChartTooltipContent />} />
          <Line dataKey="reams" type="linear" stroke="var(--color-reams)" strokeWidth={2} dot={{ r: 4, fill: "var(--color-reams)" }} activeDot={{ r: 5 }} />
        </LineChart>
      </ChartContainer>
    </ChartFigure>
  )
}

The alert colour

Give the series that needs someone color: "var(--kl-data-alert)" and leave the rest to their roles. Stacked bars take data-kl-stack on the container, which splits the segments with a gap instead of a drawn border.

Loading the live example…

"use client"

import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartFigure, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/keyline/ui/chart"

const CASES = [
  { client: "Lackawanna Co.", kept: 412, returned: 8 },
  { client: "Blue Cross", kept: 365, returned: 41 },
  { client: "Harper Collins", kept: 298, returned: 12 },
  { client: "Business Park", kept: 241, returned: 4 },
]

// returns are the figure that needs someone, so they take the alert colour
const config = {
  kept: { label: "Kept", unit: "cases" },
  returned: { label: "Returned", unit: "cases", color: "var(--kl-data-alert)" },
} satisfies ChartConfig

export function ReturnsByClient() {
  return (
    <ChartFigure title="Cases delivered this quarter" description="Returns in the alert colour; Blue Cross sent back the watermarked batch" data={CASES} config={config} labelKey="client" labelHeader="Client">
      {/* data-kl-stack splits the stacked segments with a gap */}
      <ChartContainer config={config} className="aspect-auto h-[220px]" data-kl-stack="">
        <BarChart data={CASES} layout="vertical" margin={{ top: 0, right: 8, bottom: 0, left: 0 }} barCategoryGap="28%">
          <CartesianGrid horizontal={false} />
          <XAxis type="number" tickLine={false} />
          <YAxis type="category" dataKey="client" tickLine={false} axisLine={false} width={104} />
          <ChartTooltip content={<ChartTooltipContent />} />
          <ChartLegend content={<ChartLegendContent />} />
          <Bar dataKey="kept" stackId="cases" fill="var(--color-kept)" />
          <Bar dataKey="returned" stackId="cases" fill="var(--color-returned)" />
        </BarChart>
      </ChartContainer>
    </ChartFigure>
  )
}

Props

PropTypeDefaultDescription
configChartConfigDefault —One entry per series: label, unit and color. Colours default by order to --kl-data-1, -2, -3; marks use var(--color-<key>).
titleReactNodeDefault —On ChartFigure: names the chart and its plot.
descriptionReactNodeDefault —On ChartFigure: a line under the title.
data · labelKey · labelHeaderrows · string · ReactNodeDefault —On ChartFigure: the rows behind the chart, which field names each category, and that column's header in the Table view.
defaultView"chart" | "table"Default "chart"On ChartFigure: the starting view.
format(v: number) => stringDefault grouped, 2 decimalsFormats figures in the tooltip and the table.
aria-labelstringDefault —On ChartContainer used alone, without a figure to name it.

Size the plot with CSS: it is 16:9 by default, or set a height and aspect-auto. Under reduced motion, series don't animate. ChartTable and ChartLegendContent are exported too. Install Recharts 3 with it; the registry item does.

Sheet 33 of 73PreviousTableNextStat and meter
On this page