Line Chart
Free12 variants: line, spline, step, dashed, custom animation, inversed, zones, multi-colored, stacked, 100% stacked, trend lines and strip lines. A missing reading is drawn as a break in the line, not as a zero. Pure SVG.
import { SvLineChartComponent } from '@styloviz/charts';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Spline with subtitle
<sv-line-chart [categories]="days" [series]="series" chartType="spline" title="Active users" subtitle="Last 30 days" [height]="300" />
A reading that was never taken
// null is a gap: the line breaks there rather than joining across it. series = [{ label: 'Sensor', data: [12, 14, null, 19, 21] }];
Preset — time series with zoom
// Long ordered series you scrub through. Dots off (they turn to noise past a // few hundred points), grid on, brush on, animation off so a re-render during // a drag does not fight the drag. options = { showDots: false, showGrid: true, animated: false }; // plus [zoomable]="true" [minZoomPoints]="3" on the Pro chart.
Preset — annotated analytical
// A short series someone is reading closely: every point marked and labelled, // zones recolouring the line across the thresholds that matter. options = { showDots: true, showDataLabels: true, showGrid: true, animated: true }; // plus per-series zones: [{ start: 0, end: 40, color: '#f59e0b', label: 'Below target' }]
Preset — compact sparkline
// A trend in a table cell or a stat card: no axes, no grid, no legend, nothing // but the shape. Reach for <sv-sparkline> if that is all you need. options = { showDots: false, showGrid: false, showLabels: false, showLegend: false, animated: false };
When to use
- Showing a trend or change over a continuous/ordered axis (time series).
- You want multi-series comparison with zones, trend lines or markers.
- For category comparisons rather than trends, use the Bar Chart.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| categories | string[] | [] | Category labels shown on the axis. |
| series | LineChartSeries[] | [] | Data series. |
| chartType | LineChartType | 'line' | Visual chart type. |
| title | string | '' | Chart title. |
| subtitle | string | '' | Chart subtitle / source attribution shown below the title. |
| height | number | 300 | Chart height in px. |
| options | LineChartOptions | {} | Chart-wide display options. |
| customClass | string | '' | Additional CSS classes on the root element. |
| ariaLabel | string | '' | Accessible-name override for the chart image. When empty, a concise data summary is generated; the full data is exposed via a visually-hidden table. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
<sv-line-chart-pro>
Also in this family — SvLineChartProComponent from @styloviz-pro/charts.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| categories | string[] | [] | Category labels shown on the axis. |
| series | LineChartSeries[] | [] | Data series. |
| chartType | LineChartType | 'line' | Visual chart type. |
| title | string | '' | Chart title. |
| subtitle | string | '' | Chart subtitle / source attribution shown below the title. |
| height | number | 300 | Chart height in px. |
| options | LineChartOptions | {} | Chart-wide display options. |
| customClass | string | '' | Additional CSS classes on the root element. |
| ariaLabel | string | '' | Accessible-name override for the chart image. When empty, a concise data summary is generated; the full data is exposed via a visually-hidden table. |
| zoomable | boolean | false | Shows a draggable brush (range-selector) strip below the chart for zooming and panning the visible category window. Drag a handle to zoom, drag the window itself to pan — one control surface for both, since a brush IS the pan/zoom mechanism, not a third separate feature. Normal layout only (`chartType !== 'inversed'`) — the transposed axes an inversed chart uses would need a vertical brush, out of scope for now. |
| minZoomPoints | number | 2 | Fewest categories that may stay visible when zoomed all the way in — keeps a drag from collapsing the window to nothing. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
| zoomRangeChange | LineChartZoomRange | The visible window changed — a brush drag (handle resize or pan) finished, a keyboard nudge was applied, or `resetZoom()` was called. Indices are into the full `categories`/series `data` arrays, so a host can slice its own source data to match. |
<sv-trend-line-chart>
Also in this family — SvTrendLineChartComponent from @styloviz/charts.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| title | string | '' | Chart title shown in the header; also the base of the generated accessible name. |
| subtitle | string | '' | Secondary line shown below the title. |
| categories | string[] | [] | X-axis category labels; each series' `data` is indexed by position against these. |
| series | TrendLineSeries[] | [] | Scatter series drawn as dots (optionally connected); `data` holds one value per category, and `null` or non-finite values are gaps left out of the fit. |
| trends | TrendLineDefinition[] | [{ type: 'linear' }] | Trend lines to render. The switcher overrides `type` for the first trend. |
| options | TrendLineChartOptions | {} | Display options: height, grid, axis labels, legend, dot radius, trend-type switcher, grid divisions and draw-in animation. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
| ariaLabel | string | '' | Accessible-name override; otherwise a structure summary is generated. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
<sv-trend-line-chart-pro>
Also in this family — SvTrendLineChartProComponent from @styloviz-pro/charts.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| title | string | '' | Chart title shown in the header; also the base of the generated accessible name. |
| subtitle | string | '' | Secondary line shown below the title. |
| categories | string[] | [] | X-axis category labels; each series' `data` is indexed by position against these. |
| series | TrendLineSeries[] | [] | Scatter series drawn as dots (optionally connected); `data` holds one value per category, and `null` or non-finite values are gaps left out of the fit. |
| trends | TrendLineDefinition[] | [{ type: 'linear' }] | Trend lines to render. The switcher overrides `type` for the first trend. |
| options | TrendLineChartOptions | {} | Display options: height, grid, axis labels, legend, dot radius, trend-type switcher, grid divisions and draw-in animation. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
| ariaLabel | string | '' | Accessible-name override; otherwise a structure summary is generated. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
<sv-strip-line-chart>
Also in this family — SvStripLineChartComponent from @styloviz/charts.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| title | string | '' | Chart title shown in the header; also the base of the generated accessible name. |
| subtitle | string | '' | Secondary line shown below the title (rendered only when `title` is set). |
| categories | string[] | [] | X-axis category labels; each series' `data` is indexed by position against these. |
| series | StripSeries[] | [] | Line series; `data` holds one value per category, and `null` or non-finite values are gaps that break the line. |
| strips | StripBand[] | [] | Coloured bands drawn behind the data: horizontal bands span a Y-value range, vertical bands span a category-index range. |
| options | StripLineChartOptions | {} | Display options: height, grid, axis labels, legend, band labels, grid divisions, dots and draw-in animation. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
| ariaLabel | string | '' | Accessible-name override; otherwise a structure summary is generated. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
<sv-strip-line-chart-pro>
Also in this family — SvStripLineChartProComponent from @styloviz-pro/charts.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the series are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the empty state. |
| filtered | boolean | false | Whether a filter, not an absence of data, is why the chart is empty. |
| title | string | '' | Chart title shown in the header; also the base of the generated accessible name. |
| subtitle | string | '' | Secondary line shown below the title (rendered only when `title` is set). |
| categories | string[] | [] | X-axis category labels; each series' `data` is indexed by position against these. |
| series | StripSeries[] | [] | Line series; `data` holds one value per category, and `null` or non-finite values are gaps that break the line. |
| strips | StripBand[] | [] | Coloured bands drawn behind the data: horizontal bands span a Y-value range, vertical bands span a category-index range. |
| options | StripLineChartOptions | {} | Display options: height, grid, axis labels, legend, band labels, grid divisions, dots and draw-in animation. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
| ariaLabel | string | '' | Accessible-name override; otherwise a structure summary is generated. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
Bound to the names this component actually exposes.
<sv-line-chart [series]="data.items()" [loading]="data.loading()" [error]="data.error()" [filtered]="data.filtered()" (retry)="data.reload()" />
Host class
Drive the three async inputs from your own fetch.
// This component reports its state but does not emit a query, so drive // it from your own fetch and bind the three async inputs. readonly items = signal<Row[]>([]); readonly loading = signal(false); readonly error = signal<SvDataError | null>(null); async load(): Promise<void> { this.loading.set(true); this.error.set(null); try { this.items.set(await this.api.fetchRows()); } catch (e) { this.error.set(toSvDataError(e)); } finally { this.loading.set(false); } }
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `series`, `loading` and `error`.
- Set `filtered` when a search or filter is active. It is the difference between "you have no orders" and "no orders match this filter" — telling a customer with 4,000 orders the first one reads as a bug.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Line, spline or step interpolation.
- Zones, trend lines and strip bands.
- Multi-series with custom colors and markers.
- `null` in a series is a gap: the line breaks, no marker is drawn, the filled area stops at the break rather than painting under readings nobody took, and the axis scales to the values that exist. `NaN` and `Infinity` are treated the same way — a chart cannot tell a sensor fault from a `parseFloat('n/a')`.
Best practices
- Do: Limit to a few series; too many lines turn into spaghetti.
- Do: Label series in a legend and keep colors consistent across charts.
- Do: Start from one of the three presets above rather than assembling options from scratch. They are the combinations that are actually tested together, and the ones that go wrong are predictable: dots plus a few hundred points is noise, animation plus a zoom brush fights the drag, and values plus a dense series overlaps into unreadability.
- Do: Send null for a reading that was never taken. Do NOT send 0 — that asserts a measurement of zero, which is a different claim and one the chart will happily draw a line down to. The gap breaks the line and the chart says how many readings are missing under the plot.
- Don't: Do not interpolate across a gap before passing the data in. Joining two known readings through an unknown one draws a value nobody recorded, at full confidence, in the middle of the series — and it is indistinguishable from a real measurement once it is on the page.
- Tip: The line chart has no time zone, and that is the policy. Its x is a list of category STRINGS, so whatever they say is what the axis says — the component never parses a date, never calls toLocaleDateString, and cannot shift a label by an offset. Format your timestamps once, in the zone you mean, before they reach it. If you want the chart to do the formatting, use the date-time category variant in Other Types, which takes ISO strings and reads a bare YYYY-MM-DD as a calendar day in local time rather than as UTC midnight.
- Tip: A gap in a STACKED chart is the one case the chart cannot honour: every band above it is positioned by the running total, so the missing reading has to count as zero for the stack to add up. That substitution is disclosed under the plot. If the gaps matter more than the totals, use a plain line chart.
- Tip: Use `spline` for smooth trends and `step` for discrete state changes.
Accessibility
- The SVG is `role="img"` with an auto-generated data summary (override via `ariaLabel`), backed by an `sv-data-summary-table` of every point and series value — one row per point, one column per series.
- When `zoomable` is on, the table follows the brush window instead of the full dataset, and adds a line saying which window it is ("Showing points 6–15 of 40."). A table that listed all forty points beside a chart brushed down to ten described points that are not on screen, with no way to tell which ten were — and an alternative that contradicts the picture is worse than none. The free Line Chart has no brush, so it has neither.
- Series legend is keyboard reachable; reduced-motion disables the draw-on animation.
- A break in a line is ambiguous on sight — "nothing was recorded here" to one reader, a rendering glitch to another, and easy to miss entirely at one missing point in a dense series. The count of missing readings is stated in text under the plot, so it does not depend on noticing a gap in a drawing.
Feedback on Line Chart
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.