Custom renders

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 mortar
03 — 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