> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Line

export const GraphEmbed = ({src, story, aspectRatio, borderRadius = '14px', args}) => {
  const STORYBOOK_BASE_URL = typeof window !== 'undefined' && window.location.hostname === 'localhost' ? 'http://localhost:6006' : 'https://storybook-sdk.vercel.app';
  function buildArgsString(args) {
    if (!args) return '';
    const encodeValue = value => {
      if (value === null) return '!null';
      if (value === undefined) return '!undefined';
      if (typeof value === 'boolean') return `!${value}`;
      if (typeof value === 'number') return String(value);
      return encodeURIComponent(String(value));
    };
    const properties = Object.entries(args).map(([key, value]) => `${key}:${encodeValue(value)}`).join(';');
    return `&args=${properties}`;
  }
  const resolvedSrc = story ? `${STORYBOOK_BASE_URL}/iframe.html?id=${story}&viewMode=story&embed=1&globals=mode:readonly${buildArgsString(args)}` : src;
  const resolvedAspectRatio = aspectRatio ?? (story ? '16 / 9' : '10 / 6');
  return <iframe src={resolvedSrc} loading="lazy" allowfullscreen="true" style={{
    width: '100%',
    aspectRatio: resolvedAspectRatio,
    border: 'none',
    borderRadius,
    colorScheme: 'light'
  }} />;
};

Line charts connect observations with a path. They're ideal for showing trends and change over an ordered or continuous x-axis. In the Graphy SDK, a line chart is a `geom.line()` layer over `x`/`y` position scales.

## Basic example

<GraphEmbed story="chart-types-line-graph--simple" />

```tsx theme={null}
import { useMemo } from 'react';
import {
  GraphProvider,
  GraphRenderer,
  createSpec,
  pipe,
  geom,
  scale,
} from '@graphysdk/react';

const data = {
  columns: [
    { key: 'month', label: 'Month' },
    { key: 'revenue', label: 'Revenue' },
  ],
  rows: [
    { month: 'Jan', revenue: 1200 },
    { month: 'Feb', revenue: 1800 },
    { month: 'Mar', revenue: 2400 },
    { month: 'Apr', revenue: 1600 },
    { month: 'May', revenue: 3200 },
    { month: 'Jun', revenue: 2800 },
  ],
};

export function RevenueChart() {
  const spec = useMemo(
    () =>
      pipe(
        createSpec({ x: 'month', y: 'revenue' }),
        geom.line(),
        scale.x(),
        scale.y()
      ),
    []
  );

  return (
    <GraphProvider input={spec} data={data}>
      <GraphRenderer />
    </GraphProvider>
  );
}
```

`scale.x()` infers a **datetime** scale here — Graphy recognizes the abbreviated month names (`Jan`, `Feb`, …) as dates — while `scale.y()` infers a continuous scale from the numeric `revenue`. To treat the months as plain, evenly-spaced categories instead, use `scale.x.discrete()`.

## Multiple series

Map a categorical column to `color` to split the data into one line per category, and add a color scale:

<GraphEmbed story="chart-types-line-graph--multi-series" />

```tsx theme={null}
const data = {
  columns: [
    { key: 'month', label: 'Month' },
    { key: 'region', label: 'Region' },
    { key: 'sales', label: 'Sales' },
  ],
  rows: [
    { month: 'Jan', region: 'North', sales: 600 },
    { month: 'Jan', region: 'South', sales: 900 },
    { month: 'Jan', region: 'East', sales: 1400 },
    { month: 'Jan', region: 'West', sales: 1800 },
    { month: 'Jan', region: 'Central', sales: 2400 },
    // ...
  ],
};

const spec = pipe(
  createSpec({ x: 'month', y: 'sales', color: 'region' }),
  geom.line(),
  scale.x(),
  scale.y(),
  scale.color.palette()
);
```

`scale.color.palette()` draws series colors from Graphy's default palette. To pin specific colors, use `scale.color.discrete({ domain: ['North', 'South'], range: ['#4C6EF5', '#F76707'] })`.

`strokeWidth`, `lineType` and `alpha` are mappable aesthetics too, each with its own scale — `scale.strokeWidth.continuous()`, `scale.alpha.continuous()`, and `scale.lineType.discrete()` for per-series dash patterns. `lineType` is discrete only: mapping it to a numeric variable is an error, since interpolating between dash patterns means nothing.

To single one layer out instead, give it an `id` and scope a [style](/sdk-next/config/styling) entry to it with `{ layer }`:

```tsx theme={null}
import { createSpec, pipe, geom, scale, styles, style } from '@graphysdk/react';

const spec = pipe(
  createSpec({ x: 'month', y: 'sales' }),
  geom.line({ id: 'trend' }),
  scale.x(),
  scale.y(),
  styles({
    defaults: [style.geom.line({ strokeWidth: 4 }, { layer: 'trend' })],
  })
);
```

## Smoothing

The `interpolate` param sets the curve family. The default `'linear'` draws straight segments; `'catmull-rom'` draws a smooth curve through every point:

<GraphEmbed story="chart-types-line-graph--smooth" />

```tsx theme={null}
const spec = pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line({ params: { interpolate: 'catmull-rom' } }),
  scale.x(),
  scale.y()
);
```

## Area fill

A line draws no fill of its own. Declare a `fillAlpha` style default to add a gradient beneath it, fading from the series color to transparent at the baseline:

```tsx theme={null}
import { createSpec, pipe, geom, scale, styles, style } from '@graphysdk/react';

const spec = pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line(),
  scale.x(),
  scale.y(),
  styles({ defaults: [style.geom.line({ fillAlpha: 0.15 })] })
);
```

For a solid filled band rather than a fading gradient, use [`geom.area()`](/sdk-next/graph-types/area) instead.

## Missing values

Use `null` for a missing cell. `missingValues` controls how the path treats the gap:

<GraphEmbed story="chart-types-line-graph--missing-values-connect" />

```tsx theme={null}
const data = {
  columns: [
    { key: 'month', label: 'Month' },
    { key: 'revenue', label: 'Revenue' },
  ],
  rows: [
    { month: 'Jan', revenue: 1200 },
    { month: 'Feb', revenue: 1800 },
    { month: 'Mar', revenue: null },
    { month: 'Apr', revenue: null },
    { month: 'May', revenue: 3200 },
    { month: 'Jun', revenue: 2800 },
  ],
};

const spec = pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line({ params: { missingValues: 'connect' } }),
  scale.x(),
  scale.y()
);
```

## Points on the line

Add a `geom.point()` layer to mark every vertex. It shares the spec-level mapping, so both layers plot the same series. Pipe it after the line so the dots sit on top, and set `interactive: false` on the point layer:

```tsx theme={null}
const spec = pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line(),
  geom.point({ interactive: false }),
  scale.x(),
  scale.y()
);
```

`interactive: false` keeps the points out of hit-detection so the line owns hover — it resolves the nearest point along the x-axis and drives the tooltip. Interactive points would make hover fire only when the cursor lands directly on a dot.

## Line params reference

<ParamField path="params.interpolate" type="'linear' | 'catmull-rom'" default="linear">
  Curve family. `'catmull-rom'` smooths the line through every point.
</ParamField>

<ParamField path="params.missingValues" type="'gap' | 'connect' | 'zero'" default="gap">
  How the path handles `null` values: break at the gap (`'gap'`), span it
  (`'connect'`), or treat as zero (`'zero'`).
</ParamField>

Paint — stroke width, dash, opacity, and the fill wash — is styled through the stylesheet: `style.geom.line({ color, alpha, saturation, strokeWidth, lineType, fillAlpha })`. A line draws at `strokeWidth: 2`, `lineType: 'solid'` and no fill until you declare a `fillAlpha`.

## Related

* [Styling](/sdk-next/config/styling) — stroke width, dash, the fill wash and per-layer scoping
* [Chart types overview](/sdk-next/graph-types/index) — the full recipe list
* [Axes](/sdk-next/config/axes) — labels, ticks, grid and baseline
* [Statistics](/sdk-next/advanced/statistics) — add a `smooth` trendline
* [Data structure](/sdk-next/data-structure) — how columns feed the mapping
