# Line Multiple

> A multiple line chart tracking automated resolutions vs critical incident volume built with TanStack Charts.

A shadcn/ui block from the [@paceui](https://paceui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/paceui/tanstack-charts-line-multiple).

- Page, with a live preview: https://shadcn.exchange/r/paceui/tanstack-charts-line-multiple
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/paceui/tanstack-charts-line-multiple-light-7910aa73adf7c041.png
- Registry item: https://paceui.com/r/tanstack-charts-line-multiple.json
- Listed: 2026-10-21, source last changed 2026-10-21

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/paceui/tanstack-charts-line-multiple.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@tanstack/charts`, `@tanstack/react-charts`, `d3-array`, `d3-scale`, `d3-shape`
- Registry items: `card`

## Files

### `components/tanstack-charts/line/line-multiple.tsx`

```tsx
import { defineChart, lineY } from "@tanstack/charts";
import { d3Curve } from "@tanstack/charts/d3/shape";
import { motion } from "@tanstack/charts/motion";
import { tooltip } from "@tanstack/charts/tooltip";
import { Chart } from "@tanstack/react-charts/core";
import { useMemo } from "react";

import { max } from "d3-array";
import { scaleLinear, scalePoint } from "d3-scale";
import { curveMonotoneX } from "d3-shape";

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

const motionRenderer = motion({
    initial: "always",
    transition: { type: "spring", stiffness: 170, damping: 18, mass: 1 },
});

const chartData = [
    { month: "January", resolvedAlerts: 420, criticalAlerts: 480 },
    { month: "February", resolvedAlerts: 500, criticalAlerts: 460 },
    { month: "March", resolvedAlerts: 470, criticalAlerts: 490 },
    { month: "April", resolvedAlerts: 560, criticalAlerts: 450 },
    { month: "May", resolvedAlerts: 530, criticalAlerts: 470 },
    { month: "June", resolvedAlerts: 580, criticalAlerts: 650 },
    { month: "July", resolvedAlerts: 580, criticalAlerts: 540 },
    { month: "August", resolvedAlerts: 690, criticalAlerts: 510 },
    { month: "September", resolvedAlerts: 660, criticalAlerts: 490 },
    { month: "October", resolvedAlerts: 760, criticalAlerts: 470 },
];

export function LineMultiple() {
    const definition = useMemo(() => {
        const yMax = max(chartData, (d) => Math.max(d.resolvedAlerts, d.criticalAlerts)) ?? 0;

        return defineChart({
            renderer: motionRenderer,
            motion: {
                transition: { type: "spring", stiffness: 170, damping: 18, mass: 1 },
            },
            focus: "group-x",
            tooltip: {
                use: tooltip,
                motion: { type: "spring", stiffness: 170, damping: 18 },
                className: "font-sans! shadow-none! capitalize!",
                content: (points) => {
                    const datum = points[0]?.datum as (typeof chartData)[number];
                    if (!datum) return { rows: [] };
                    return {
                        title: String(points[0].xValue),
                        rows: [
                            {
                                label: "Resolved Alerts",
                                value: String(datum.resolvedAlerts),
                                color: "var(--chart-1)",
                            },
                            {
                                label: "Critical Alerts",
                                value: String(datum.criticalAlerts),
                                color: "var(--chart-2)",
                            },
                        ],
                    };
                },
            },
            marks: [
                lineY(chartData, {
                    id: "resolvedAlerts",
                    x: (d) => d.month.slice(0, 3),
                    y: (d) => d.resolvedAlerts,
                    stroke: "var(--chart-1)",
                    strokeWidth: 2,
                    curve: d3Curve(curveMonotoneX),
                    points: false,
                }),
                lineY(chartData, {
                    id: "criticalAlerts",
                    x: (d) => d.month.slice(0, 3),
                    y: (d) => d.criticalAlerts,
                    stroke: "var(--chart-2)",
                    strokeWidth: 2,
                    curve: d3Curve(curveMonotoneX),
                    points: false,
                }),
            ],
            scales: {
                x: {
                    scale: scalePoint().domain(chartData.map((d) => d.month.slice(0, 3))),
                },
                y: {
                    scale: scaleLinear().domain([0, yMax]).nice(),
                    grid: true,
                },
            },
        });
    }, []);

    return (
        <Card className="gap-4">
            <CardHeader>
                <CardTitle className="text-lg">Security Operations Center Metrics</CardTitle>
                <CardDescription>Automated Resolutions vs Unhandled Critical Incident Volume</CardDescription>
            </CardHeader>
            <CardContent>
                <Chart
                    definition={definition}
                    renderer={motionRenderer}
                    height={250}
                    ariaLabel="Multiple line chart showing security incident resolution trends over ten months"
                />
            </CardContent>
        </Card>
    );
}
```
