Line Graphs¶
Line graphs connect values across an ordered axis, usually time, to emphasize continuity, direction, rate of change, and overall shape; this family includes single- or multi-series lines and slope graphs. They are most useful when the main question is how something changes over time rather than how big isolated categories are.
dbt charts line graphs use a small set of top-level shorthand fields together with style. In most cases, you only need query, type: line, x, and y to get started.
A chart's control surface is the full set of authored properties available on a single chart. In dbt charts, that surface is primarily top-level chart fields plus typed style objects.
This page is intentionally family-oriented rather than exhaustive. For the implementation-backed source of truth, including default ownership and lower-level property coverage, see the YAML Schema Reference.
Minimum Required for a Line Graph¶
These are the minimum fields required to render a basic line graph in dbt charts.
| dbt charts field | Maps to Vega-Lite | Allowed values | Notes |
|---|---|---|---|
query |
data.values |
Query name or query reference | Supplies the dataset. |
type: line |
mark.type: "line" |
Literal line |
Selects the line mark. |
x |
encoding.x.field |
Field name | Ordered dimension, usually time. |
y |
encoding.y.field |
Field name | Numeric measure to plot. |
Minimum Example¶
source: examples_db
charts:
revenue_trend_minimal:
query:
sql: |
SELECT date, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Daily Revenue
x: date
y: revenue
rows:
- revenue_trend_minimal
Top-Level Chart Fields¶
These are the top-level chart properties you set directly on a line graph before you get into nested properties under style.
| dbt charts field | Maps to Vega-Lite | Allowed values | Notes |
|---|---|---|---|
query |
data.values |
Query name or query reference | Query results become the plotted dataset. |
type: line |
mark.type: "line" |
Literal line |
Selects the line mark. |
x |
encoding.x.field |
Field name | Usually a temporal or ordered field. |
y |
encoding.y.field |
Field name or list of field names | A list creates layered multi-metric lines. |
title |
title.text |
String | Chart title. |
description |
metadata | String | Available for tooling and docs. |
color |
encoding.color.field |
Field name | Groups a line into multiple series. |
x_label |
encoding.x.title |
String | Custom x-axis title. |
y_label |
encoding.y.title |
String | Custom y-axis title. |
format |
encoding.y.format and encoding.y.axis.format |
Format string | Numeric formatting for the y channel. |
sort |
categorical axis sort | Sort object | Most useful when the x-axis is categorical. |
projection |
top-level projection |
Vega-Lite projection name | Available for Vega-Lite projection overrides. |
Multi-Series Line Graph¶
This example adds color to split one line into multiple series while keeping the top-level chart definition compact.
source: examples_db
charts:
revenue_by_product:
query:
sql: |
SELECT date, product, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date, product ORDER BY date, product
type: line
title: Revenue by Product
x: date
y: revenue
color: product
rows:
- revenue_by_product
Axis Labels, Formatting, and Styling¶
This example shows common top-level chart fields such as labels and numeric formatting.
source: examples_db
charts:
revenue_formatted:
query:
sql: |
SELECT date, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Revenue Over Time
x: date
y: revenue
x_label: Date
y_label: Revenue
style:
number_format: currency_whole
axis_y:
scale:
continuous:
zero: false
axis:
grid:
visible: false
rows:
- revenue_formatted
Layered Multi-Metric Lines¶
When y is a list, dbt charts creates a layered line graph. This is useful when you want to compare two measures that share the same ordered x-axis.
source: examples_db
charts:
revenue_and_units:
query:
sql: |
SELECT date, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Revenue And Units Sold
x: date
y: [revenue, units_sold]
rows:
- revenue_and_units
Style Fields¶
Use style for dbt charts shorthand properties that affect presentational defaults such as legend visibility and grid lines.
| dbt charts field | Maps to Vega-Lite | Allowed values | Notes |
|---|---|---|---|
style.legend.visible |
encoding.color.legend |
true or false |
Typed legend control. false hides the legend. |
style.axis.grid.visible |
axis grid visibility | true or false |
false hides grid lines. Use style.axis.grid.visible: false (nested under grid:). |
style.background |
chart SVG background wrapper | Color value | Applies a chart-level background fill behind the rendered SVG. |
Legend and Grid Lines¶
This example shows the small style surface for line graphs without changing the underlying data bindings.
source: examples_db
charts:
revenue_formatted:
query:
sql: |
SELECT date, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Revenue Over Time
x: date
y: revenue
x_label: Date
y_label: Revenue
style:
number_format: currency_whole
axis_y:
scale:
continuous:
zero: false
axis:
grid:
visible: false
rows:
- revenue_formatted
Axis and Scale Style¶
Use style for axis and scale properties that shape how the line graph is framed and read.
| dbt charts field | Maps to Vega-Lite | Allowed values | Notes |
|---|---|---|---|
style.axis_x |
config.axisX / encoding.x.axis |
AxisStyle object |
Per-axis styling (format, ticks, labels, grid, title). |
style.axis_y |
config.axisY / encoding.y.axis |
AxisStyle object |
Per-axis styling. |
style.axis_y.scale |
config.axisY.scale |
ScaleStyle object |
Y-axis scale config (zero, nice, domain, clamp). |
Axis and Scale Controls¶
This example uses style to control the y-axis scale and tick density.
source: examples_db
charts:
revenue_formatted:
query:
sql: |
SELECT date, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Revenue Over Time
x: date
y: revenue
x_label: Date
y_label: Revenue
style:
number_format: currency_whole
axis_y:
scale:
continuous:
zero: false
axis:
grid:
visible: false
rows:
- revenue_formatted
Log Scale¶
Set style.axis_y.scale.continuous.type: log to plot a quantitative axis on a logarithmic scale — useful when values span multiple orders of magnitude (e.g. exponential growth). Log scales are undefined at zero and for negative numbers; dbt charts validates the domain at compile time and raises ERR_LOG_SCALE_REQUIRES_POSITIVE_DATA if any data value is zero or negative. Only use axis_y.scale.continuous.type: log on a measure you know is strictly positive.
source: examples_db
charts:
revenue_log:
query:
sql: |
SELECT date, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Revenue Over Time (Log Scale)
x: date
y: revenue
x_label: Date
y_label: Revenue
style:
number_format: currency_whole
axis_y:
scale:
continuous:
type: log
rows:
- revenue_log
Endpoint Labels¶
Set style.endpoint_labels.visible: true on a multi-series line graph to move the series names off the side legend and onto the chart, anchored to each line's final point. The chart pane and label pane sit side-by-side in an hconcat, with one label per series.
| dbt charts field | Allowed values | Notes |
|---|---|---|
style.endpoint_labels.visible |
true or false |
Opt-in feature toggle. Defaults to false on every built-in theme — set per-chart when you want it. |
When the label pane is on, the categorical legend turns off automatically — the two would encode the same series→colour mapping twice. The y-axis also auto-flips to the left so it doesn't collide with the right-edge label pane.
On narrow cards, endpoint-label text compacts to fit the available width while its automatic vertical spacing stays readable for closely ending series.
Endpoint labels require a multi-series chart — either a color channel or a y: [field_a, field_b] list. On single-series lines there is only one series to name, so the feature is a no-op.
You cannot combine y: [...] with an authored color: channel or layers:; dbt charts rejects that combination at compile time with a clear error.
source: examples_db
charts:
revenue_by_product:
query:
sql: |
SELECT date, product, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY date, product ORDER BY date, product
type: line
title: Revenue by Product
x: date
y: revenue
color: product
style:
endpoint_labels:
visible: true
rows:
- revenue_by_product
Smoothing & Step Lines¶
style.marks.line.curve controls how adjacent points are joined, in place of
the default straight-segment interpolation:
| Value | Effect |
|---|---|
monotone |
A smooth curve that never overshoots the data — no wobble past a local min or max. A softer read than straight segments without misrepresenting the trend. |
step |
On a categorical (nominal/ordinal) x-axis, a full-band-width plateau per x-value instead of a diagonal segment. On a continuous (temporal/quantitative) x-axis, Vega-Lite's own native step interpolate (points at band centers). |
source: examples_db
charts:
revenue_smoothed:
query:
sql: |
SELECT date, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Daily Revenue (Smoothed)
x: date
y: revenue
style:
marks:
line:
curve: monotone
rows:
- revenue_smoothed
Step Lines¶
curve: step on a categorical x column draws a full-band-width plateau.
Adjacent plateaus are joined by a vertical bridge by default; set
connect: false to leave them as disconnected segments instead.
Disconnected plateaus end flush with the band edge — each one is its own path,
so the default round line cap would overhang every edge by half the stroke
width and close up the gap between neighboring bands. Set
disconnected_cap: round under marks.line to opt back into rounded ends.
Connected step lines are unaffected: they are a single continuous path, so
stroke.cap still governs their two outer ends.
source: examples_db
charts:
revenue_by_product_step:
query:
sql: |
SELECT product, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY product ORDER BY revenue DESC
type: line
title: Product Revenue (Step)
x: product
y: revenue
style:
marks:
line:
curve: step
connect: false
rows:
- revenue_by_product_step
Halo¶
Multi-series lines that cross each other can be hard to trace right at the
crossing point. style.marks.line.halo_multiplier draws a knockout-colored
stroke behind each line, sized relative to the line's own stroke width, so
crossings stay legible. Set it to 0 to disable the halo.
Value Labels¶
Show the numeric value beside each data point on a line chart by enabling style.marks.line.labels.visible.
source: examples_db
charts:
revenue_labeled:
query:
sql: |
SELECT date, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY date ORDER BY date
type: line
title: Daily Revenue
x: date
y: revenue
style:
marks:
line:
labels:
visible: true
position: top # top | bottom | left | right | middle
format: ",.1f" # omit to inherit axis_quantitative.format
font:
color: "#6b7280"
size: 11
rows:
- revenue_labeled
Position values:
| Value | Placement |
|---|---|
top |
Above the point — default |
bottom |
Below the point |
left |
To the left of the point |
right |
To the right of the point |
middle |
Centered on the point |
dx/dy (pixel offsets) are also available under labels to nudge a label off its default position. Line and point charts share the same position vocabulary and Vega-Lite mapping.
On a curve: step line over a categorical x, the mark is a plateau spanning the whole
band rather than a point, so left and right resolve against the band's leading and
trailing edge instead of the datum at its center — otherwise the caption would land on
top of the mark it is meant to sit beside. This tracks the real band width, so no dx
is needed to clear the mark at any board width or category count. Where the band is the
first or last in the domain and the preferred side has no room, that one label falls
back to top, keeping it inside its own band rather than over its neighbor's.
top, bottom and middle are unaffected — band width is horizontal, and the band's
center is the mark's center.
Overlays (layers:)¶
Add a layers: list to a line chart to overlay additional marks — a bar for context, a
scatter reference, or a second line on a separate y-axis. The base line chart owns the
x-axis, frame, title, legend, and sort. Each layer contributes its own mark and legend
entry.
charts: trend_with_target: type: line x: month y: actual query: monthly layers: - type: line y: target label: Target axis_y: position: right title: Target style: stroke: width: 2 dash: [4, 2]
For a dual-axis example where the overlay draws from a separate query:
charts: signups_and_conversion: type: line x: month y: signups query: monthly_signups layers: - type: line y: conversion_pct label: Conversion (%) query: weekly_conversion axis_y: position: right title: Conversion (%) scale: continuous: domain: [0, 100]
Layer x-values from a separate query extend the base x-scale automatically — no x_domain field is needed or accepted.
Layer fields: type, y, label, color, query, x, axis_y, style (marks-only patch). sort: is base-only.
Authored Surface¶
dbt charts line charts are authored with type: line plus top-level channels such as x, y, and color. Arbitrary Vega-Lite spec, mark, encoding, config, transform, params, and composition blocks are rejected on the authored surface. Use top-level dbt charts fields and the typed style: object.