When to use line
Line is the right choice when the x-axis is ordered and the question is about trend — typically a time series (daily revenue, monthly orders, hourly errors) or any other naturally ordered category. The same renderer covers three shapes:
- Single line — one measure plotted across the x-axis. The default.
- Dual-axis — two measures on different scales sharing one chart. Add
mapping.y2; the second measure renders against a right-hand y-axis. - Multi-series — one line per category value, all sharing the same y-axis. Add
mapping.serieswith the categorical field.
y2 and series are mutually exclusive in intent: dual-axis is for two measures on the same x; multi-series is for one measure split by a category. Do not combine them in one viz.
Use bar instead when the x-axis is unordered and the question is about comparison. Use scatter when the relationship is between two continuous measures rather than a measure across an ordered axis.
Mapping
mapping.x— required. The ordered axis field (date, month, integer, ordinal category).mapping.y— required. Numeric field for the (left) y-axis values.mapping.y2— optional. Numeric field. When present, the chart switches to dual-axis mode.mapping.series— optional. Categorical field. When present, the chart switches to multi-series mode.mapping.series_label— legend / tooltip label for the primary y series. Default"value".mapping.series_label_2— legend / tooltip label for the y2 series in dual-axis mode. Default"y2".
# single line
mapping:
x: order_month
y: revenue
series_label: Revenue
# dual-axis
mapping:
x: order_month
y: revenue
y2: order_count
series_label: Revenue
series_label_2: Orders
# multi-series (one line per channel)
mapping:
x: order_month
y: revenue
series: channel
chart shortcuts
The chart block is typed and closed — anything not listed in this page is rejected at validation time.
chart.show_value_labels— boolean. Turns on data-point labels for every series. Style them withchart.value_labelbelow.chart.cross_filter— boolean, defaulttrue. Setfalseto suppress click-to-filter for this viz.chart.height— pixel height of the viz container.
Line intentionally exposes a small option surface — most styling decisions are taken from the platform theme. Series-level customisation goes through mapping, not chart.
Value labels
chart.value_label is an object applied to every series when chart.show_value_labels is on:
position— placement of the label relative to the point. Common values:"top","bottom","right","insideTop","insideBottom".rotate— degrees, between -90 and 90.color,font_size,font_weight.formatter— string template (no callbacks). Use{c}for the value.distance,align,vertical_align,clip.
Value labels on a line are noisy for dense series; consider showing them only on the last point by post-processing the data, or rely on the hover tooltip instead.
Legend & tooltip
chart.legend:
show— boolean.position— shortcut:"top","bottom","left","right","top-left","top-right","bottom-left","bottom-right".orient—"horizontal"|"vertical".top/bottom/left/right— pixel number or percent string.text_style—color,font_style,font_weight,font_family,font_size,line_height.item_width,item_height,item_gap.
chart.tooltip:
show— boolean.trigger—"item","axis"(recommended for line — shows all series at the hovered x), or"none".confine,formatter,background_color,border_color,border_width,padding,text_style.axis_pointer.type—"line"(recommended for line),"shadow","none","cross".
Axes
chart.x_axis and chart.y_axis share the same shape (with one extra on x_axis):
name— axis title.name_location—"start"|"middle"|"center"|"end".name_gap— pixels between axis line and name.axis_label.show— boolean.axis_label.rotate— degrees, -90 to 90.axis_label.interval— integer or"auto".axis_label.color,axis_label.font_size,axis_label.font_weight.axis_label.formatter— string template.axis_label.max_chars— integer ≥ 1, ellipsizes long labels.x_axis.visible_window— integer ≥ 1. Restricts the visible range and enables a horizontal range slider; useful for long time series.
format
format.yorformat[<y_field_name>]— pattern for the left y-axis labels and tooltip values.format.y2orformat[<y2_field_name>]— pattern for the right y-axis (dual-axis mode).formatat the root — fallback.
format:
revenue: "$#,##0"
order_count: "#,##0"
Cross-filter behavior
Inside a dashboard, clicking a line cross-filters the rest of the dashboard. Full mechanism at Cross-filtering. Line specifics:
- In single / dual-axis mode, clicking a point cross-filters by the clicked x value.
- In multi-series mode, clicking a line cross-filters by the series name.
- The clicked field must be declared as a parameter in at least one model used by the dashboard, otherwise the click is silently ignored.
- Disable per viz with
chart.cross_filter: false.
Worked examples
Single line, with a rotated x-axis label for monthly periods:
id: ec_revenue_over_time_line
title: Revenue Over Time
query: "models/ec_revenue.malloy::over_time"
type: line
mapping:
x: order_month
y: revenue
series_label: Revenue
chart:
height: 320
x_axis:
axis_label:
rotate: -30
y_axis:
name: Revenue
tooltip:
trigger: axis
axis_pointer:
type: line
format:
revenue: "$#,##0"
published: true
Dual-axis (revenue on the left, order count on the right):
id: ec_revenue_orders_line
title: Revenue and Orders
query: "models/ec_revenue.malloy::over_time"
type: line
mapping:
x: order_month
y: revenue
y2: order_count
series_label: Revenue
series_label_2: Orders
chart:
height: 320
legend:
show: true
position: top
format:
revenue: "$#,##0"
order_count: "#,##0"
published: true
Multi-series (one line per channel):
id: ec_revenue_by_channel_line
title: Revenue by Channel
query: "models/ec_revenue.malloy::by_channel_over_time"
type: line
mapping:
x: order_month
y: revenue
series: channel
chart:
legend:
show: true
position: top
tooltip:
trigger: axis
format:
revenue: "$#,##0"
published: true
Long time series with a horizontal data-zoom slider:
chart:
x_axis:
visible_window: 30
Common pitfalls
- Mixing dual-axis and multi-series. Setting both
y2andseriesin the same viz produces undefined behavior — pick one. - Dual-axis labels collide. Two value scales need room. Set explicit
name_gapon both axes, reduce data density, or split into two charts. - Multi-series legend is too wide. If the categorical field has many distinct values, the legend can dominate the chart. Move it to
position: bottom, or filter to top-N in the underlying Malloy query. - Date formatting on the x-axis is wrong. Format the x value with
format[<x_field_name>]or pre-format in the Malloy query (e.g. derive anorder_month_labelstring). - Time-zone shifts in the x values. Time-series x values are interpreted in the user's session timezone. Pre-bucket dates to days or months in the Malloy query if you need consistent groupings across users.
- Cross-filter clicks have no effect. The clicked field must be declared as a parameter in at least one model used by the dashboard. Without that, clicks are silently ignored.