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
1Jan'2423456789101112131415161718192021222324252627282930010,00020,00030,000Daily Revenue Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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.
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
1Jan'242345678910111213141516171819202122232425262728293005,00010,000Revenue by ProductWidget ATool ZGadget XWidget CWidget BGadget ZGadget YTool A Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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
1Jan'2423456789101112131415161718192021222324252627282930Date$10,000$20,000$30,000RevenueRevenue Over Time Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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
1Jan'2423456789101112131415161718192021222324252627282930010,00020,00030,00040,000Revenue and Units Soldrevenueunits_sold Data as of 14:52 UTC on 21 Aug 2026 made with dbt charts

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
1Jan'2423456789101112131415161718192021222324252627282930Date$10,000$20,000$30,000RevenueRevenue Over Time Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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
1Jan'2423456789101112131415161718192021222324252627282930Date$10,000$20,000$30,000RevenueRevenue Over Time Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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 (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
1Jan'2423456789101112131415161718192021222324252627282930Date$1,000$2,000$3,000$4,000$5,000$10,000$20,000$30,000$40,000$50,000$100,000RevenueRevenue Over Time (Log Scale) Data as of 14:52 UTC on 21 Aug 2026 made with dbt charts

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
1Jan'242345678910111213141516171819202122232425262728293005,00010,000Revenue by ProductWidget ATool ZGadget XWidget CWidget BGadget ZGadget YTool A Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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
1Jan'2423456789101112131415161718192021222324252627282930010,00020,00030,000Daily Revenue (Smoothed) Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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 BGadget YWidget AGadget XWidget CTool ZTool AGadget Z20,00040,00060,000Product Revenue (Step) Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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, 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
1Jan'2423456789101112131415161718192021222324252627282930010,00020,00030,0008,640.017,007.011,502.019,365.019,904.013,108.013,324.06,653.012,017.014,480.09,312.014,414.033,191.07,402.012,919.08,429.09,574.06,770.08,222.019,717.020,549.019,756.013,756.017,097.015,834.014,174.020,298.010,207.032,028.013,374.0Daily Revenue Data as of 14:52 UTC on 21 Aug 2026 made with dbt 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:
          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.