# Line Chart

> A composable line chart with tooltips, markers, and hover interactions

Source: https://ui.cortexcn.dev/docs/charts/line-chart

## Installation

```bash
npx shadcn@latest add @cortexcn/line-chart
```

## Usage

Build charts by composing components. See the [charts gallery](https://ui.cortexcn.dev/charts/line-chart) for interactive examples.

```tsx
import { LineChart, Line, Grid, XAxis, ChartTooltip } from "@/components/charts";

const data = [
  { date: new Date("2025-01-01"), users: 1200 },
  { date: new Date("2025-01-02"), users: 1350 },
  // ...
];

export default function SimpleChart() {
  return (
    <LineChart data={data}>
      <Grid horizontal />
      <Line dataKey="users" />
      <XAxis />
      <ChartTooltip />
    </LineChart>
  );
}
```

## Updating data smoothly

When your data changes (e.g. filtering by date range, refreshing from an API), the chart automatically updates smoothly without replaying the enter animation. The y-domain tweens to the new scale via `yDomainTween` (enabled by default).

**Do not** change or pass the `revealSignature` prop when updating data. That prop is only for manually replaying the reveal animation.

Click the date range buttons — the line and y-axis morph smoothly without reinitialization. Use the replay button to run the enter animation again.

## Components

### LineChart

The root component that provides context to all children.

| Prop                          | Type                        | Default                                        | Description                                                     |
| ----------------------------- | --------------------------- | ---------------------------------------------- | --------------------------------------------------------------- |
| `data`                        | `Record<string, unknown>[]` | required                                       | Array of data points                                            |
| `xDataKey`                    | `string`                    | `"date"`                                       | Key in data for x-axis values                                   |
| `margin`                      | `Partial<Margin>`           | `{ top: 40, right: 40, bottom: 40, left: 40 }` | Chart margins                                                   |
| `animationDuration`           | `number`                    | `1100`                                         | Clip-reveal duration in ms (`cubic-bezier(0.85, 0, 0.15, 1)`)   |
| `status`                      | `"loading" \| "ready"`      | `"ready"`                                      | Loading ↔ ready choreography on one chart instance              |
| `loadingLabel`                | `string`                    | —                                              | Centered shimmer label while `status="loading"` (`""` hides it) |
| `yDomainTween`                | `boolean`                   | `true`                                         | Animate y-domain when status or target domain changes           |
| `yDomainTweenDuration`        | `number`                    | `500`                                          | Y-domain tween duration in ms                                   |
| `xDomain`                     | `[Date, Date]`              | —                                              | Visible x-range for brush zoom                                  |
| `xDomainSlotCount`            | `number`                    | —                                              | Full dataset length for x-scale padding when `xDomain` is set   |
| `tweenYDomainOnXDomainChange` | `boolean`                   | `false`                                        | Tween y-domain when the brush changes the visible x-range       |
| `aspectRatio`                 | `string`                    | `"2 / 1"`                                      | CSS aspect ratio                                                |
| `className`                   | `string`                    | `""`                                           | Additional CSS class                                            |
| `style`                       | `CSSProperties`             | —                                              | Inline container styles (e.g. fixed height for a brush strip)   |

### Line

Renders a line on the chart.

| Prop                   | Type                     | Default                     | Description                                                                  |
| ---------------------- | ------------------------ | --------------------------- | ---------------------------------------------------------------------------- |
| `dataKey`              | `string`                 | required                    | Key in data for y values                                                     |
| `yAxisId`              | `string \| number`       | `"left"`                    | Y-scale group for biaxial charts (pair with `YAxis`)                         |
| `stroke`               | `string`                 | `var(--chart-line-primary)` | Line color                                                                   |
| `strokeWidth`          | `number`                 | `2.5`                       | Line width                                                                   |
| `curve`                | `CurveFactory`           | `curveNatural`              | D3 curve function                                                            |
| `animate`              | `boolean`                | `true`                      | Enable grow animation                                                        |
| `fadeEdges`            | `boolean`                | `true`                      | Fade line at edges                                                           |
| `showHighlight`        | `boolean`                | `true`                      | Show highlight on hover                                                      |
| `showMarkers`          | `boolean`                | `false`                     | Render scatter-style ring markers at each point                              |
| `loadingStroke`        | `string`                 | `var(--foreground)`         | Pulse stroke color while chart is loading                                    |
| `loadingStrokeOpacity` | `number`                 | `0.5`                       | Pulse stroke opacity while chart is loading                                  |
| `markers`              | `SeriesPointMarkerStyle` | —                           | Marker styling (same options as [`Scatter`](https://ui.cortexcn.dev/docs/charts/scatter-chart)) |

### Grid

Renders grid lines.

| Prop                          | Type       | Default                                 | Description                                                                       |
| ----------------------------- | ---------- | --------------------------------------- | --------------------------------------------------------------------------------- |
| `horizontal`                  | `boolean`  | `true`                                  | Show horizontal lines                                                             |
| `vertical`                    | `boolean`  | `false`                                 | Show vertical lines                                                               |
| `numTicksRows`                | `number`   | `5`                                     | Number of horizontal lines                                                        |
| `numTicksColumns`             | `number`   | `10`                                    | Number of vertical lines                                                          |
| `stroke`                      | `string`   | `var(--chart-grid)`                     | Line color while ready                                                            |
| `loadingStroke`               | `string`   | —                                       | Grid stroke while loading chrome is active                                        |
| `strokeDasharray`             | `string`   | `"4,4"`                                 | Dash pattern                                                                      |
| `highlightRowValues`          | `number[]` | —                                       | Draw emphasized horizontal lines at specific y values (e.g. `[0]` for break-even) |
| `highlightRowStroke`          | `string`   | `var(--chart-foreground-muted)`         | Stroke for highlighted rows                                                       |
| `highlightRowStrokeOpacity`   | `number`   | `1`                                     | Opacity for highlighted rows                                                      |
| `highlightRowStrokeWidth`     | `number`   | `1`                                     | Width for highlighted rows                                                        |
| `highlightRowStrokeDasharray` | `string`   | `"0"`                                   | Dash pattern for highlighted rows (`"0"` = solid)                                 |
| `shimmer`                     | `boolean`  | `false`                                 | Animate a shimmer band across horizontal grid lines                               |
| `shimmerStroke`               | `string`   | `color-mix(…)` on `--foreground` at 68% | Shimmer band color and opacity                                                    |
| `shimmerLength`               | `number`   | `140`                                   | Shimmer band width in pixels                                                      |
| `shimmerSpeed`                | `number`   | `1`                                     | Shimmer speed multiplier when sync is off (higher = faster)                       |
| `shimmerSync`                 | `boolean`  | `false`                                 | Match shimmer timing to the line pulse (2.2s cycle + 280ms pause)                 |

### Background

Pattern fill for the plot area when you omit `Grid`. Fades in after the series reveal on time-series charts. See the [Background utility](https://ui.cortexcn.dev/docs/utility/background) for presets (`diagonal`, `dots`, `cross`, …), edge fade, and opacity — and the **Pattern Background** examples on the [line chart gallery](https://ui.cortexcn.dev/charts/line-chart).

### XAxis

Renders x-axis labels that fade when the crosshair passes.

| Prop              | Type                 | Default  | Description                                                                                  |
| ----------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------- |
| `numTicks`        | `number`             | `6`      | Number of tick labels                                                                        |
| `tickerHalfWidth` | `number`             | `50`     | Fade radius for labels                                                                       |
| `tickMode`        | `"data" \| "domain"` | `"data"` | `"data"` snaps labels to data rows (crosshair-aligned); `"domain"` for calendar-even spacing |

### ChartTooltip

Renders the tooltip with crosshair, dots, and content box.

| Prop                  | Type                                    | Default  | Description                                                    |
| --------------------- | --------------------------------------- | -------- | -------------------------------------------------------------- |
| `showDatePill`        | `boolean`                               | `true`   | Show animated date ticker                                      |
| `showCrosshair`       | `boolean`                               | `true`   | Show vertical crosshair                                        |
| `showDots`            | `boolean`                               | `true`   | Show dots on lines                                             |
| `indicatorColor`      | `string \| (point) => string`           | —        | Crosshair and dot color; use a function for value-based colors |
| `indicatorDasharray`  | `string`                                | —        | Dash pattern for the crosshair (e.g. `"4,4"`)                  |
| `indicatorFadeEdges`  | `"both" \| "top" \| "bottom" \| "none"` | `"both"` | Vertical crosshair fade                                        |
| `indicatorFadeLength` | `number`                                | `10`     | Fade size (% of height)                                        |
| `matchCrosshair`      | `boolean`                               | `false`  | Panel uses crosshair spring when `true`                        |
| `damping`             | `number`                                | `20`     | Panel follow when `matchCrosshair={false}`; `0` = instant      |
| `content`             | `(props) => ReactNode`                  | -        | Custom content renderer                                        |
| `rows`                | `(point) => TooltipRow[]`               | -        | Custom row generator                                           |

## Dual Y axes (biaxial)

Pair `yAxisId` on each `Line` with matching `YAxis` components. Increase `margin.left` and `margin.right` so labels fit. See [Y Axis](https://ui.cortexcn.dev/docs/utility/axis/y-axis) and the **Left and right Y axes** examples on the [line chart gallery](https://ui.cortexcn.dev/charts/line-chart).

## Profit/Loss line

For a single series that crosses zero, use [`ProfitLossLine`](https://ui.cortexcn.dev/docs/charts/profit-loss-line) inside `LineChart`. Pair it with a hidden `Line` (same `dataKey`) so the chart registers the series for the y-domain and tooltip. When any value is negative, the y-axis automatically includes the full data extent instead of anchoring at zero.

Highlight the break-even baseline with `Grid highlightRowValues={[0]}`:

```tsx
<Grid
  highlightRowValues={[0]}
  highlightRowStroke="var(--foreground)"
  highlightRowStrokeOpacity={0.35}
  horizontal
/>
```

See the [Profit/Loss Line](https://ui.cortexcn.dev/docs/charts/profit-loss-line) docs and the [line chart gallery](https://ui.cortexcn.dev/charts/line-chart) (**Profit/Loss** example).

## Projection

Extend a series past the last data point with [`ProjectionLine`](https://ui.cortexcn.dev/docs/utility/projection-line). Build the path with `buildProjectionPath` (auto slope, target value, or manual points). Optionally add [`LineSeriesTerminalMarker`](https://ui.cortexcn.dev/docs/utility/projection-line#terminal-marker) at the anchor.

Projections extend the x-domain to the horizon and are not supported together with [brush zoom](https://ui.cortexcn.dev/docs/utility/brush).

<div className="not-prose mb-3 flex items-center justify-between gap-4">
  <h3 className="m-0 font-semibold text-foreground text-base tracking-tight">
    Preview
  </h3>
</div>

See the [Projection Line utility](https://ui.cortexcn.dev/docs/utility/projection-line) for props and `buildProjectionPath` options.

## Brush zoom

Use `ChartBrushLayout` and `ChartBrush` the same way as on [`AreaChart`](https://ui.cortexcn.dev/docs/charts/area-chart#brush-zoom). See the [Brush](https://ui.cortexcn.dev/docs/utility/brush) utility docs for full API reference. The brush strip typically shows simplified `Line` series; the main chart receives `xDomain`, `xDomainSlotCount`, and `tweenYDomainOnXDomainChange` for live zoom and y-domain tweening.

<div className="not-prose mb-3 flex items-center justify-between gap-4">
  <h3 className="m-0 font-semibold text-foreground text-base tracking-tight">
    Preview
  </h3>
</div>

## Loading state

Drive loading and ready from your data layer with a single `LineChart` — one `Grid`, one `Line`, no component swap. Set `status="loading"` while fetching; switch to `"ready"` when data resolves.

**Loading → ready:** pulse loop on skeleton data → pulse finishes its grow, then flows out right → loading label drifts down 30px, blurs, and fades → grid y-domain tween (500ms) → clip-path reveal (`cubic-bezier(0.85, 0, 0.15, 1)`) → interaction enabled.

**Ready → loading:** ready line conceals to the right → grid y-domain tween → pulse loop and shimmer resume.

Pair `Grid` `stroke` / `loadingStroke` with shimmer props. Pair `Line` `loadingStroke` props. Use `loadingLabel` on `LineChart` for centered shimmer text via `@cortexcn/shimmering-text`.

<div className="not-prose mb-3 flex items-center justify-between gap-4">
  <h3 className="m-0 font-semibold text-foreground text-base tracking-tight">
    Preview
  </h3>
</div>

Toggle **Loading** / **Ready** in the preview to replay the transition, and **Pulse** / **Sweep** to switch the loading animation style. When target data spans a different y-range than the skeleton, `yDomainTween` morphs the scale before the line reveals.

Installing `@cortexcn/line-chart` pulls in `@cortexcn/shimmering-text` automatically.

### Loading style: pulse or sweep

The loading state has two animation styles, set with `loadingStyle` on the `Line`: the default `"pulse"` (a segment travels along the skeleton stroke) or `"sweep"` (a soft diagonal shimmer sweeps across the whole line). Set it on the `Line` inside a `status="loading"` chart, or on the `LineChartLoading` wrapper:

```tsx
<LineChart data={data} status="loading">
  <Grid horizontal shimmer />
  <Line dataKey="revenue" loadingStyle="sweep" />
</LineChart>

// or, with the turnkey wrapper:
<LineChartLoading loadingStyle="sweep" />;
```

The sweep masks over the real skeleton line, so it follows whatever `curve` the `Line` uses (`curveStepAfter`, `curveNatural`, …) and respects `prefers-reduced-motion`. To keep the loading→ready handoff smooth, the sweep is used only during steady loading; the pulse still drives the exit transition. See the **Loading (Sweep)** example on the [line chart gallery](https://ui.cortexcn.dev/charts/line-chart).

## Dashed tail

Set `dashFromIndex` on `Line` to draw a solid stroke through one data point, then a dashed segment through the end of the series. Useful when the final period is still in progress (e.g. yesterday → today).

`dashFromIndex` is **inclusive** — dashing starts at that row and continues through the last point. The dashed segment follows the same curved path as the solid stroke and respects `fadeEdges`.

| Prop            | Type     | Default | Description                                         |
| --------------- | -------- | ------- | --------------------------------------------------- |
| `dashFromIndex` | `number` | —       | Inclusive data index where the dashed tail begins   |
| `dashArray`     | `string` | `"6,4"` | SVG `stroke-dasharray` pattern for the tail segment |

```tsx
<Line
  dataKey="visitors"
  dashFromIndex={5}
  dashArray="6,4"
  stroke="var(--chart-line-primary)"
/>
```

## Markers

Add markers to annotate specific dates on the chart:

```tsx
import {
  LineChart,
  Line,
  ChartTooltip,
  ChartMarkers,
  MarkerTooltipContent,
  useActiveMarkers,
  type ChartMarker,
} from "@/components/charts";

const markers: ChartMarker[] = [
  {
    date: new Date("2025-01-05"),
    icon: "🚀",
    title: "v1.2.0 Released",
    description: "New chart animations",
  },
  {
    date: new Date("2025-01-05"), // Same day - will stack!
    icon: "🐛",
    title: "Bug Fix",
    description: "Fixed tooltip positioning",
  },
];

function MyChart({ data }) {
  return (
    <LineChart data={data}>
      <Line dataKey="users" />
      <ChartMarkers items={markers} />
      <ChartTooltip>
        <MarkerContent markers={markers} />
      </ChartTooltip>
    </LineChart>
  );
}

// Use the hook to get markers for the hovered date
function MarkerContent({ markers }) {
  const activeMarkers = useActiveMarkers(markers);
  if (activeMarkers.length === 0) return null;
  return <MarkerTooltipContent markers={activeMarkers} />;
}
```

### ChartMarker Interface

```ts
interface ChartMarker {
  date: Date; // Date for marker position
  icon: React.ReactNode; // Icon (emoji or component)
  title: string; // Tooltip title
  description?: string; // Optional description
  content?: React.ReactNode; // Custom tooltip content
  color?: string; // Background color override
  onClick?: () => void; // Click handler
  href?: string; // URL to navigate to
  target?: "_blank" | "_self"; // Link target
}
```

### ChartMarkers Props

| Prop        | Type            | Default  | Description                 |
| ----------- | --------------- | -------- | --------------------------- |
| `items`     | `ChartMarker[]` | required | Array of markers            |
| `size`      | `number`        | `28`     | Marker circle size          |
| `showLines` | `boolean`       | `true`   | Show vertical guide lines   |
| `animate`   | `boolean`       | `true`   | Animate markers on entrance |

## Segment Selection

Add click-drag and touch segment selection with composable components. The line highlight automatically shows the selected path segment.

### Basic Usage

Click and drag (or two-finger touch on mobile) to select a range:

```tsx
import {
  LineChart,
  Line,
  Grid,
  XAxis,
  ChartTooltip,
  SegmentBackground,
  SegmentLineFrom,
  SegmentLineTo,
} from "@/components/charts";

<LineChart data={data}>
  <Grid horizontal />
  <Line dataKey="users" />
  <SegmentBackground />
  <SegmentLineFrom />
  <SegmentLineTo />
  <XAxis />
  <ChartTooltip />
</LineChart>;
```

Use `SegmentBackground`, `SegmentLineFrom`, and `SegmentLineTo` independently — you do not need all three. Boundary lines support `variant="dashed" | "solid" | "gradient"`.

### Reading Selection Data

Use the `useChart` hook inside a child component to read the active selection:

```tsx
import { useChart } from "@/components/charts";

function SelectionStats({ onSelectionChange }) {
  const { selection, data, xAccessor } = useChart();

  useEffect(() => {
    if (!selection?.active) {
      onSelectionChange(null);
      return;
    }

    const startPoint = data[selection.startIndex];
    const endPoint = data[selection.endIndex];
    // Compute and report stats...
    onSelectionChange({ startPoint, endPoint });
  }, [selection, data, xAccessor, onSelectionChange]);

  return null;
}
```

### SegmentBackground

| Prop   | Type     | Default                           | Description                        |
| ------ | -------- | --------------------------------- | ---------------------------------- |
| `fill` | `string` | `var(--chart-segment-background)` | Fill color for the selected region |

### SegmentLineFrom / SegmentLineTo

| Prop          | Type                                | Default                     | Description |
| ------------- | ----------------------------------- | --------------------------- | ----------- |
| `stroke`      | `string`                            | `var(--chart-segment-line)` | Line color  |
| `strokeWidth` | `number`                            | `1`                         | Line width  |
| `variant`     | `"dashed" \| "solid" \| "gradient"` | `"dashed"`                  | Line style  |

## Theming

The chart uses CSS variables for theming. Define these in your CSS:

```css
:root {
  --chart-background: oklch(1 0 0);
  --chart-foreground: oklch(0.145 0.004 285);
  --chart-foreground-muted: oklch(0.55 0.014 260);
  --chart-line-primary: oklch(0.623 0.214 255);
  --chart-line-secondary: oklch(0.705 0.015 265);
  --chart-crosshair: oklch(0.4 0.1828 274.34);
  --chart-grid: oklch(0.9 0 0);
  --chart-tooltip-foreground: oklch(0.985 0 0);
  --chart-tooltip-muted: oklch(0.65 0.01 260);
  --chart-marker-background: oklch(0.97 0.005 260);
  --chart-marker-border: oklch(0.85 0.01 260);
  --chart-marker-foreground: oklch(0.3 0.01 260);
  --chart-marker-badge-background: oklch(0 0 0);
  --chart-marker-badge-foreground: oklch(1 0 0);
  --chart-segment-background: oklch(0.5 0 0 / 0.06);
  --chart-segment-line: oklch(0.5 0 0 / 0.25);
}

.dark {
  --chart-background: oklch(0.145 0 0);
  --chart-foreground: oklch(0.45 0 0);
  --chart-crosshair: oklch(0.45 0 0);
  --chart-grid: oklch(0.25 0 0);
  --chart-marker-background: oklch(0.25 0.01 260);
  --chart-marker-border: oklch(0.4 0.01 260);
  --chart-marker-foreground: oklch(0.9 0 0);
  --chart-marker-badge-background: oklch(1 0 0);
  --chart-marker-badge-foreground: oklch(0.15 0 0);
  --chart-segment-background: oklch(1 0 0 / 0.06);
  --chart-segment-line: oklch(1 0 0 / 0.25);
}
```

## Dependencies

This component requires the following packages:

```bash
pnpm add @visx/shape @visx/curve @visx/scale @visx/gradient @visx/responsive @visx/event @visx/grid d3-array motion react-use-measure
```