Skip to main content
Live example: app/demo/axes-and-grid.tsx in the example app.

Grid lines, axis spacing, and edge labels

Grid & axes

Pass yAxis={false} or xAxis={false} to hide an axis. gridStyle.intervals of [] renders solid grid lines. xAxis.minGap sets the minimum pixel spacing between time labels (default 60). Changing it updates the label set immediately on both chart components, including paused charts and views parked by scrolling or zooming. Labels can be spaced farther apart when their text would otherwise overlap. The right gutter is measured with your formatValue and font once a finite, nonzero live value arrives. A 0 loading placeholder does not lock it to a short label. It measures again after the value moves by a factor of ten in either direction from the last sample, keeping ordinary ticks and small changes around a power of ten from moving the plot’s edge. LiveChartSeries samples the largest finite absolute value across its series, including negative values alongside zero placeholders. An explicit insets.right overrides automatic measurement.

Vertical grid lines

xAxis.gridLines adds a vertical line under each time label, so the grid runs in both directions. The lines draw behind the series, in the same layer as the horizontal grid, and fade in and out with their labels at the plot edges.
gridLines: true styles the lines with gridStyle. A GridStyleConfig overrides only the fields it sets; the rest still come from gridStyle. The lines follow the label spacing (xAxis.minGap), run down through a candle chart’s volume band to the axis line, and hide with the axis (xAxis={false}) and under LiveChart’s axisAutoHide. They are off by default, on both LiveChart and LiveChartSeries. Strokes are clipped to the plot width, so a fading tick cannot draw a grid line into the reserved Y-axis gutters.

Left-side Y-axis labels

Both LiveChart and LiveChartSeries accept yAxis.side; it defaults to "right". Reserve enough room for your widest formatted label with insets.left:
Labels are centered in the left gutter. Its width is explicit rather than measured automatically, so account for your font and formatter. Grid lines still span the plot. Right-label options (float, labelRightMargin, gridEndGap) do not position left labels. The live badge and multi-series value labels remain on their existing side. Left labels render after the left-edge fade so the gutter stays readable.

Fixed Y-axis price count

By default the Y axis picks a dynamic number of “nice”-value grid lines from minGap. Pass yAxis={{ count }} to instead show a fixed number of prices, spaced evenly down the plot — top label = current high, bottom = current low. The count stays constant as data streams in; the values track the live range each frame (they aren’t rounded to nice numbers).
minGap still acts as a floor: if count labels won’t fit at least minGap px apart, the count drops to what fits. The count is clamped to at most 15.

Nice intervals in display units

Use intervalScale when stored values and displayed values differ by a constant multiplier. The chart keeps every point in its original units, but chooses dynamic grid intervals after applying the multiplier. Apply the same multiplier in formatValue:
This can turn labels such as $9,876,543 and $10,864,198 into round $10,000,000 and $11,000,000 intervals. The chart geometry, markers, reference lines, and scrub values stay in their original units. The multiplier must be positive and finite; invalid values behave as 1. If applying it would overflow or underflow the current live range, interval selection safely falls back to source units. Fixed-count mode ignores it because { count } tracks the exact visible range instead of choosing nice intervals.

Minimum Y range

On a short window the data can move less than one display step, so every grid label formats to the same text. Pass minRange (in data units) to keep the fitted span at least that tall; flatter data widens around its midpoint instead of zooming in:
Data that already spans more than minRange fits as before. On a nonNegative chart or under maxValue, the span slides off the bound instead of shrinking, including when a bound clips the ordinary fit’s margin. If both hard bounds leave less room than the requested floor, the available span wins. Use positive finite values; invalid or overflowing floors are ignored. Manual yRangeScale zoom still applies on top and can go below the floor. LiveChartSeries accepts it too. The Minimum Y range demo compares rounded labels, both engines, bounds, static re-settling, and manual zoom.

Right-anchored price label column

Set labelRightMargin to place every Y-axis label at the same left X in a column whose right edge is measured from the canvas edge. Add gridEndGap to keep a fixed gap between the grid-line end and that column:
For a right-anchored column, both chart components measure the actual tick strings on the UI thread. Automatic right padding reserves that width plus the canvas-edge margin, the largest dot/pulse/glow radius, and six points of clearance. An initial formatted sample adds half a font-size of precision headroom until ticks exist. Layout updates on the React thread only when the measured width changes. Badge and inline series-label requirements can reserve additional space; explicit insets.right takes precedence. Without labelRightMargin, labels keep their default centered-gutter placement and gridEndGap has no effect. When combined with yAxis.float, labelRightMargin overrides the floating axis’s default edge margin. Plain horizontal referenceLines stop at the same X as the grid, keeping the gap consistent. fullWidth reference lines and value/time bands keep their existing extents.

Badge value in the column

By default the live badge centers its value in the pill. To line it up with the label column, set badge={{ textAlign: "yAxisColumn" }}: the value starts at the labels’ left X, and a value wider than every axis label ends at the column’s right edge. Two trade-offs make this opt-in:
  • The pill keeps its shape and the value stays within its padX padding, so the room on each side comes from the right inset and the badge metrics. Next to the tail, whose taper already keeps the value off the outline, a pill too narrow for both paddings gives up the left one first, then the right one, so the value moves continuously as the pill narrows. With an inset of dotGap, the tail (tailLength plus half the pill’s height), the value, padX and marginEdge, the value ends padX from the pill’s end (the column’s right edge when marginEdge + padX equals labelRightMargin). The value’s width is Skia’s measureText box, which can be wider than the glyph advances. A value wider than a tailed pill’s body, or one that doesn’t fit both paddings of a pill without a tail, is centered. The automatic right inset reserves room for one more digit, which ends up left of the value. For even padding, size insets.right for your widest price and set metrics.badge so marginEdge + padX equals labelRightMargin.
  • The value moves sideways whenever the column’s width changes: a wider label entering or leaving the axis (99.95 → 100.00), or digits of different widths in a proportional font.
It applies to the default right-gutter badge with the column on the right. With yAxis.side: "left", while the axis floats (yAxis.float; with timeScroll, only while scrolled back), and with badge={{ position: "left" }}, the value stays centered. badge.offsetX moves the value with the pill. To place a pill relative to the column yourself, turn the badge off and anchor your own pill to the column in renderOverlay, as the Phantom showcase does for its target pill. For an interactive comparison of centered and column-aligned values, including the transition across $100 and padding/font fallbacks, open the Badge alignment demo.

Axis edge labels

topLabel and bottomLabel float a value at the top / bottom edge of the plot — the Robinhood-style high/low readout. Pass true for the batteries-included label: the chart shows its current top (topLabel) and bottom (bottomLabel) Y-axis bound, formatted with the chart’s formatValue and updated each frame on the UI thread.
Both default off. Pass an AxisLabelConfig to tune the built-in label — format, color, position ("left" | "right", default "right"), and the text fontSize / fontWeight / fontFamily:
For full control, the render escape hatch floats any element at the edge (it overrides the built-in value):
To pin the label to the actual high / low data point instead of the edge — position: "extrema" / "extrema-edge" — see Extrema labels.

Spacing

Adjust the drawing area with insets (top, right, bottom, left) and soften the left edge with leftEdgeFade.