Sometimes you need to plot two things on the same timeline that simply do not belong to the same scale. Revenue and headcount. Inflation and GDP. Price and spread. If you force them onto one axis, one series becomes unreadable, and the chart turns into a polite lie.
A dual-axis chart solves this by keeping a shared X axis (time) while providing two independent Y axes, so each metric is shown in its natural units. The goal is not to pretend the metrics are comparable. The goal is to compare when they move and how their dynamics relate.
In RareCharts, DualAxes is exactly that: a time-based chart with two vertical scales.
To illustrate a real-world use case, this example uses a finance-native story: a convergence (arbitrage) strategy based on the idea that two similar instruments that temporarily diverged in price will later converge again.
One concrete version of this story exists in government bonds, where a newly issued “on-the-run” Treasury can trade slightly richer than the previous “off-the-run” issue for a period of time. Traders may position for convergence by going long the cheaper issue and hedging the richer one, expecting the price gap to compress as the market normalizes.
In the chart, the right axis (Y1) shows the price index of the two instruments over time. The left axis (Y2) shows the spread between them. Prices and spread are different categories, so they get different axes. The chart stays readable, and the relationship stays visible.
Axis behavior and formatting
Both axes are configured independently. You can set titles (y1Title, y2Title), control tick formatting (y1TickFormat, y2TickFormat), and, when needed, override the visible ranges (y1Domain, y2Domain) to keep the chart stable and comparable across screenshots, reports, or multiple panels.
In this example, the spread axis uses a signed format and treats very small values as clean zero, so you do not get the infamous +0.00 noise that makes charts look broken even when the data is fine.
Interaction
Dual Axes supports cursor inspection through a crosshair and a tooltip. The tooltip is fully customizable through tooltipFormat ({ date, points }), so you can present values in your product’s language instead of whatever generic tooltip someone thought was “good enough”.
Dual-axis charts are often implemented as a hack: two scales, mismatched formatting, confusing labels, and tooltips that quietly mix units. This component exists to make the dual-axis case predictable, explicit, and safe for real reporting: independent scales, consistent structure, and controlled formatting.
Dual Axes chart options
Common options shared by all chart types (title, subtitle, legend, legendPosition, source, theme) are documented on the Settings page.
| Option | Type | Default | Description |
|---|---|---|---|
Layout |
|||
height |
number | 280 |
Chart height in px. |
margin |
object | — |
Inner padding {top, right, bottom, left}.Right defaults to 92, left to 64 to give both axes room.
|
xPad |
number | 8 |
Extra horizontal padding on both ends of the X scale. |
Right axis — Y1Y1 is drawn on the right side. |
|||
y1Domain |
[min, max] |
auto |
Override the Y1 scale domain. Useful for keeping charts comparable across snapshots. |
y1Ticks |
number | 4 |
Tick count on Y1. |
y1TickFormat |
function | ,.2f |
(value) => string — Y1 tick labels. |
y1LabelsOnly |
boolean | true |
Show only tick labels; suppress the axis line. |
y1Title |
string | — | Axis title, rendered along the right edge. |
Left axis — Y2Y2 is drawn on the left side. Grid lines and the zero baseline are keyed to Y2. |
|||
y2Domain |
[min, max] |
auto | Override the Y2 scale domain. |
y2Ticks |
number | 4 |
Tick count on Y2. |
y2TickFormat |
function | +.2f |
(value) => string — Y2 tick labels. |
y2LabelsOnly |
boolean | true |
Show only tick labels; suppress the axis line. |
y2Title |
string | — | Axis title, rendered along the left edge. |
X axis |
|||
xTickFormat |
function | '%m/%d' |
(date) => string — X tick labels. |
Lines |
|||
curve |
string | 'linear' |
D3 curve type for line series: 'linear', 'monotone', 'step', etc. |
curveTension |
number | 0 |
Tension for the 'cardinal' curve, 0–1. |
strokeDash |
string | — | SVG stroke-dasharray applied to all line series globally. |
area |
boolean | false |
Fill area under line series. |
areaOpacity |
number | 0.12 |
Area fill opacity. |
areaBaseline |
'zero' | 'min' | number |
'zero' |
Area baseline anchor. |
Bars |
|||
barOpacity |
number | 0.35 |
Bar fill opacity. |
barWidthRatio |
number | 0.65 |
Bar width as a fraction of the time step width. |
barGrouping |
'overlap' | 'cluster' |
'overlap' |
How multiple bar series are arranged. 'cluster' places them side by side. |
Visibility |
|||
showGrid |
boolean | true |
Show horizontal grid lines. |
showXAxis |
boolean | true |
Show the X (date) axis at the bottom. |
showY1Axis |
boolean | true |
Show the Y1 axis on the right. |
showY2Axis |
boolean | true |
Show the Y2 axis on the left. |
End labels and markers |
|||
endLabels |
boolean | true |
Show last-value labels at the right edge for line series. |
endLabelsAxis |
'y1' | 'y2' |
'y1' |
Which axis to use when formatting end labels. |
markers |
boolean | false |
Point markers at each data sample on line series. |
markerShape |
string | 'circle' |
Marker shape: 'circle', 'square', 'diamond'. |
markerSize |
number | 4 |
Marker radius in px. |
Interaction |
|||
crosshair |
boolean | true |
Vertical tracker with dots at data points and a tooltip on hover. |
tooltipFormat |
function | — |
({ date, points }) => html — where
points is [{name, value, color, fmt}].
|
Animation |
|||
animate |
boolean | true |
Animate on first render. |
duration |
number | 650 |
Animation duration in ms. |
ease |
string | 'cubicOut' |
Easing: 'cubicOut', 'cubicInOut', 'linear'. |
Per-series fieldsEach object in the series array passed to |
|||
name |
string | 'Series N' |
Series name — used in legend and tooltip. |
axis |
'y1' | 'y2' |
'y1' |
Which Y axis to plot against. |
type |
'line' | 'bar' |
'line' |
Rendering type for this series. |
color |
CSS color | theme palette | Series color. |
strokeWidth |
number | 2 |
Line thickness in px (lines only). |
strokeDash |
string | — | SVG dash pattern for this series only. |
curve |
string | global curve |
Curve override for this series. |
area |
boolean | global area |
Fill area under this series. |
areaOpacity |
number | global areaOpacity |
Per-series area opacity. |
areaBaseline |
string | number | global areaBaseline |
Per-series area baseline. |
values |
array | — | [{date, value}, ...] — the data points. |