A custom renderer in Wick Charts isn't a fork or a subclass — it's a small
candlePainter function that owns the pixels of each element while the engine keeps doing layout, scales, pan / zoom and the crosshair. Here we build one that draws candle bodies as live-streaming Renko bricks; the brick data itself is an ordinary transform (see Source).BTC/USDRenko · live · custom candle painter
012345678901234567890123456789.0123456789012345678901234567890123456789
100102104106
00:0012:0000:00
01 — A PAINTER IS THE RENDERER
A custom renderer here is just a function — no subclass, no fork. The engine keeps owning layout, scales, pan / zoom, axes and the crosshair; it resolves the exact bitmap rectangle each element occupies and hands your painter the live 2D context. Everything inside that rect is yours to draw.
const renkoBrick: CandlePainter = (env, args) => { // env: live 2D context + device-pixel ratios + theme const { ctx, horizontalPixelRatio, verticalPixelRatio } = env; // args: the rect the engine resolved + semantics (color, isBullish, radius...) const { geom, color } = args; // ...draw onto ctx, within geom};02 — DRAW THE ELEMENT YOURSELF
Own the pixels. Here each candle body becomes a Renko brick: inset the engine's rect by one device pixel on every side so neighbours separate into a brick wall, then fill with the semantic
color. It's a raw canvas — it could just as easily be an arc, a gradient, or a sprite.const x = geom.x + horizontalPixelRatio;const y = geom.y + verticalPixelRatio;const w = Math.max(1, geom.width - horizontalPixelRatio * 2);const h = Math.max(1, geom.height - verticalPixelRatio * 2); ctx.fillStyle = color;ctx.fillRect(x, y, w, h); // the inset gap reads through as mortar03 — HAND IT TO THE SERIES
Pass the function straight to
candlePainter — that's the whole wiring. It's read fresh every frame; keep it a stable reference (module scope or useCallback) so the wrapper diffs it cleanly. Feed it any data — here, brick OHLC from an ordinary toRenko transform (see Source).<CandlestickSeries data={bricks} options={{ candlePainter: renkoBrick, bodyWidthRatio: 1 }}/>04 — IT STREAMS LIKE ANY SERIES
Owning the pixels doesn't opt you out of live updates. The painter has no coupling to where bars come from — it runs every frame on whatever bricks exist. The demo re-derives bricks from the close history on each tick (
toRenko is pure, so existing bricks diff as no-ops) and hands the window to the data prop; a freshly cleared box fades in with the series' entrance animation.const next = toRenko(closes, grid);if (next.length === brickCount) return; // no box cleared brickCount = next.length;setBricks(next.slice(-WINDOW)); // diffed → appended → painter fades it in