Skip to content

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
Jan2024FebMarAprMayJun0   ​20   ​40   ​60   ​80 KDaily Revenue Data as of 14:24 UTC on 6 Oct 2026 made withdbt Charts
▶

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.
notes metadata String AI search/context metadata. Never rendered.
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.
field_labels legend, tooltip and axis titles Map of column to text Display text for a column, painted as written. See Field Labels.
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_TRUNC('week', date) AS week, product, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY week, product ORDER BY week, product
    type: line
    title: Revenue by Product
    x: week
    y: revenue
    color: product
rows:
  - revenue_by_product
Jan2024FebMarAprMayJun0   ​50   ​100   ​150 KRevenue by ProductGadget XWidget BWidget AGadget YTool ZWidget CTool AGadget Z Data as of 14:24 UTC on 6 Oct 2026 made withdbt Charts
▶

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
Jan2024FebMarAprMayJunDate$20,000$40,000$60,000$80,000RevenueRevenue Over Time Data as of 14:24 UTC on 6 Oct 2026 made withdbt Charts
▶

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
Jan2024FebMarAprMayJun0   ​20   ​40   ​60   ​80 KRevenue and Units Soldrevenueunits sold Data as of 14:24 UTC on 6 Oct 2026 made withdbt Charts
▶

A pivoted result (several measures grouped by a dimension) adds color: naming the dimension; see Multiple Measures by a Dimension.

Field Labels

Every surface that names a column (legend entries and titles, tooltip rows, axis titles) derives its text from the column name: units_sold becomes units sold (title-cased in legend titles). field_labels maps a column to the text to paint instead, written exactly as given. x_label/y_label and a layer's label still win on their own surface. A key that names no column of the chart's query is an error (checked when the query returns rows). Tables use style.columns.<col>.label instead.

charts:
  revenue_and_units_labeled:
    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]
    field_labels:
      revenue: Revenue (USD)
      units_sold: Units shipped
rows:
  - revenue_and_units_labeled

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
Jan2024FebMarAprMayJunDate$20,000$40,000$60,000$80,000RevenueRevenue Over Time Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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
Jan2024FebMarAprMayJunDate$20,000$40,000$60,000$80,000RevenueRevenue Over Time Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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 (for example, 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
Jan2024FebMarAprMayJunDate$10,000$20,000$30,000$40,000$50,000$60,000$70,000$80,000$90,000$100,000RevenueRevenue Over Time (Log Scale) Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Endpoint Labels

A multi-series line graph names its series on the chart, anchored to each line's final point, rather than in a side legend. This is the default on every built-in theme; you don't switch it on. Set style.endpoint_labels.visible: false to send the names back to a legend.

dbt Charts field Allowed values Notes
style.endpoint_labels.visible true or false Defaults to true on every built-in theme. Set false per-chart to send the series names back to a legend.

When the label pane is on, the categorical legend turns off automatically: the two would encode the same series→color 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.

y: [...] composes with a color: column: the measures are crossed with the column's values, one series per value per measure, each named <value> - <measure>; see Multiple Measures by a Dimension. layers: still cannot combine with y: [...]; dbt Charts rejects that at compile time.

source: examples_db
charts:
  revenue_by_product:
    query:
      sql: |
        SELECT DATE_TRUNC('week', date) AS week, product, SUM(revenue) AS revenue, SUM(units_sold) AS units_sold FROM ecommerce_orders GROUP BY week, product ORDER BY week, product
    type: line
    title: Revenue by Product
    x: week
    y: revenue
    color: product
    style:
      endpoint_labels:
        visible: true

rows:
  - revenue_by_product
Jan2024FebMarAprMayJun0   ​50   ​100   ​150 KRevenue by ProductGadget XWidget BWidget AGadget YTool ZWidget CTool AGadget Z Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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
Jan2024FebMarAprMayJun0   ​20   ​40   ​60   ​80 KDaily Revenue (Smoothed) Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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
Widget BWidget AGadget YGadget XTool ZWidget CTool AGadget Z500k1,000k1,500kProduct Revenue (Step) Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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_TRUNC('week', date) AS week, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY week ORDER BY week
    type: line
    title: Weekly Revenue
    x: week
    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
Jan2024FebMarAprMayJun300   ​350   ​400 K279,890.0283,211.0277,678.0278,013.0274,131.0284,742.0279,719.0284,287.0283,364.0293,816.0301,371.0309,152.0321,348.0325,336.0337,921.0339,630.0349,830.0354,669.0371,279.0373,752.0378,526.0396,101.0402,591.0423,189.0432,212.0435,740.0Weekly Revenue Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

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:
          marks:
            line:
              stroke:
                width: 2
                dasharray: "4 2"

A layer on its own axis gets an independent scale; see Dual y-axis for why dbt Charts warns about it and what to use instead.

For a dual-axis example where the overlay draws from a separate query:

queries:
  monthly_signups:
    columns: [month, signups]
    values:
      - ["2024-01", 480]
      - ["2024-02", 560]
  weekly_conversion:
    columns: [month, conversion_pct]
    values:
      - ["2024-01", 12.5]
      - ["2024-02", 14.2]
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:
            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.


Multiple Measures by a Dimension

A pivoted result, several measures as columns grouped by a dimension, is the shape every semantic layer and most warehouse rollups produce. Add color: naming the dimension and each of its values is crossed with each measure: two measures by three categories draws six lines, each named <category> - <measure>. The legend, endpoint labels, and tooltip all read those composite names.

source: examples_db
charts:
  order_size_by_category:
    query:
      sql: |
        SELECT date, category, AVG(revenue) AS avg_order_value, MAX(revenue) AS largest_order FROM ecommerce_orders GROUP BY date, category ORDER BY date, category
    type: line
    title: Average And Largest Order by Category
    x: date
    y: [avg_order_value, largest_order]
    color: category
rows:
  - order_size_by_category
Jan2024FebMarAprMayJun1,0002,0003,000Average and Largest Order by …Accessories - largest orderElectronics - largest orderTools - largest orderTools - Avg order valueAccessories - Avg order val…Electronics - Avg order value Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶