ECharts Candlestick with Volume Panel — Free Linked-Chart Snippet

ECharts Candlestick with Volume Panel · Charts · Plain HTML, CSS & JS · Live preview

What's included

Features

Linked dual-panel layout
One chart instance, two grids, one shared x-axis range.
Synced zoom across panels
A single dataZoom drives both the price and volume x-axes.
Synced crosshair
axisPointer.link aligns hover position across both panels.
Color-matched volume bars
Volume color derives from that day's own candle direction.
Standard bullish/bearish coloring
color/color0 pair distinguishes up and down candles.
Currency and magnitude formatting
Price axis in dollars, volume axis in millions.
Deterministic OHLC generator
Same 60-day price path renders on every load.
Responsive canvas
ResizeObserver keeps both panels sized to their container.

About this UI Snippet

ECharts Candlestick with Volume Panel — Two Charts, One Synced Zoom

Screenshot of the ECharts Candlestick with Volume Panel snippet rendered live

A price chart without volume is missing half the story — a big move on thin volume reads very differently from the same move on heavy volume. This snippet renders both as two panels in one ECharts instance, sharing a single x-axis and zoom control, which is the standard layout for any real trading interface.

Two grids in one option object

The grid array holds two entries — a tall one for the candles, a short one beneath it for volume bars — each with its own top/height. Every axis and series then references which grid it belongs to via gridIndex (defaulting to 0 for the candlestick's axes). This is how ECharts stacks two logically separate charts inside a single canvas and a single echarts.init call, instead of syncing two independent chart instances by hand.

One zoom control, two x-axes

The dataZoom entries list xAxisIndex: [0, 1], meaning a single slider (or scroll-zoom) drives both x-axes at once. Drag the slider and the candlestick panel and the volume panel scroll through exactly the same date range together — critical, since a volume bar that doesn't line up with its candle is worse than no volume chart at all.

axisPointer.link syncs the crosshair too

Beyond zoom, axisPointer: { link: [{ xAxisIndex: 'all' }] } keeps the hover crosshair aligned across both panels — move the mouse over the candlestick panel and the volume panel's crosshair tracks the same x position, so the tooltip (triggered by axis) can show both series' values for one date together.

Volume bars inherit the candle's color

Each volume bar's color is computed from that same day's close-vs-open, not set independently — ohlc[i][1] >= ohlc[i][0] ? UP : DOWN. That's what makes a quick glance at the volume panel alone tell you whether heavy days were up days or down days, without looking back up at the candles.

itemStyle.color0 is the "down" color

ECharts' candlestick series takes *two* colors per item style: color/borderColor for a close-above-open (bullish) candle, and color0/borderColor0 for a close-below-open (bearish) one — both are required, since a candlestick chart with only one color pair would be unable to show direction at all.

Reusing it

Swap the generated OHLC and volume arrays for real market data (from any exchange API returning daily bars), and the linked zoom, linked crosshair, and color-matched volume all keep working unchanged, since they're wired through axis and grid indices rather than any ticker-specific logic.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out multi-panel axis linking from the ECharts docs alone. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the grid array and gridIndex properties stack two logically separate charts inside one canvas, and how dataZoom's xAxisIndex array and axisPointer.link keep the zoom range and hover crosshair synchronized between the price and volume panels. The same assistant can help optimize it — ask whether disabling animation (animation: false) is the right call for a chart this data-dense, and whether computing each volume bar's color from that day's own OHLC values inline is cleaner than precomputing a separate colors array. It's also useful for extending the effect: ask it to add a moving-average overlay line on the candlestick panel, a third linked panel for a technical indicator like RSI, or a way to switch the ticker and refetch new OHLC data without re-initializing the whole chart. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a candlestick price chart with a linked volume panel underneath using Apache ECharts (load echarts from a CDN, no other library), in plain HTML, CSS, and JavaScript.

Requirements:
- Generate or accept about 60 days of OHLC (open, high, low, close) price data plus a corresponding trading volume number for each day, with prices following a continuous path where each day's movement builds on the previous day's close (not fully independent random values each day).
- Render the OHLC data as a candlestick chart in the upper portion of the chart area, using one color for days that closed higher than they opened and a different color for days that closed lower than they opened.
- Render the volume data as a bar chart in a separate, shorter panel directly beneath the candlestick panel, within the same single chart instance (not a second separate chart), coloring each volume bar to match that same day's candle direction (up-color or down-color) rather than using one uniform color for all bars.
- Give both panels their own x-axis but link them so that a single zoom control (both a slider below the chart and scroll/pinch-to-zoom directly on the chart) changes the visible date range on both panels simultaneously and keeps every candle aligned with its own volume bar at any zoom level.
- Link the hover crosshair/axis pointer across both panels so that hovering over a point in either panel shows an aligned indicator at the same x position in the other panel too.
- Format the price axis labels with a currency symbol and the volume axis labels in abbreviated form (e.g. "1.2M" instead of "1200000").
- Keep the chart instance responsive to its container being resized by calling the chart's resize method whenever the container's size changes.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="ecs-wrap">
  <div class="ecs-card">
    <div class="ecs-title">TICKER — Daily Candles</div>
    <div class="ecs-sub">Drag the bottom slider — the volume panel below zooms in sync</div>
    <div class="ecs-chart" id="ecsChart"></div>
  </div>
</div>

Step by step

How to Use

  1. 1
    Add the ECharts CDNLoad echarts.min.js before the snippet's JS runs.
  2. 2
    Paste HTML, CSS, and JSSixty days of candles render above a matching volume panel.
  3. 3
    Drag the sliderBoth panels zoom to the same date range together.
  4. 4
    Hover a candleA synced crosshair appears in the volume panel below it.
  5. 5
    Read the volume colorsGreen bars are up days, red bars are down days.
  6. 6
    Compare a big move to its volumeSee whether a price swing had heavy or thin volume behind it.

Real-world uses

Common Use Cases

Trading and finance dashboards
The standard price-plus-volume layout for any ticker.
Crypto and token trackers
Same OHLC-plus-volume model, any asset class.
Portfolio and watchlist detail views
Drill into one holding's recent price action.
Backtesting and research tools
Zoom into a specific window to inspect a trade signal.
Financial reporting
Pair with a revenue line chart for business-side metrics nearby.
Learning ECharts
A clear reference for multi-grid, axis-linked layouts.
Related: ECharts Live-Updating Realtime Chart
See the ECharts Live-Updating Realtime Chart for a related charts pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The grid option is an array with two entries — one tall region for the candlesticks, one short region beneath it for volume — each given its own top and height. Every axis and series is then assigned to one of those grids via gridIndex (0 is the default candlestick grid, 1 is the volume grid), which is how ECharts renders what looks like two charts from a single option object and a single echarts.init call.

Both dataZoom entries specify xAxisIndex: [0, 1], meaning a single zoom control (whether the slider or scroll-inside) applies to both x-axes simultaneously rather than just the one it visually sits under. Dragging the slider moves the visible date range on both the candlestick and volume x-axes together, keeping every candle aligned with its own volume bar at all zoom levels.

Candlestick series (and, by extension, any related coloring) distinguish bullish days (close at or above open) from bearish days (close below open) using two separate color properties: color/borderColor for bullish, color0/borderColor0 for bearish. The volume bars reuse this same up/down logic per day by comparing that day's own open and close values, coloring each bar to match its candle rather than using one fixed color for all volume.

dataZoom controls which range of dates is visible; axisPointer.link controls the hover crosshair, keeping it aligned to the same x position across both grids when you move your mouse over either panel. Without it, hovering the candlestick panel would show a crosshair only there, leaving the volume panel with no indication of which date the tooltip is currently describing.

Fetch or compute your OHLC and volume arrays outside the chart option, initialize the chart once against a container ref, and call setOption with fresh series data whenever a new bar arrives (for streaming data) or the selected ticker changes. Dispose the instance on unmount and keep the ResizeObserver pattern, since a two-panel layout is especially sensitive to needing an explicit resize call when its container changes size.