Custom renders

Are you an AI agent?
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).
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 element to draw, the rects the engine resolved + semantics
// (bodyColor, isBullish, radius...)
const { element, body, bodyColor } = args;
// ...draw onto ctx, within body
};
02 — DRAW THE ELEMENT YOURSELF
Own the pixels. The painter runs once per wick, then once per body. A brick has no wick, so that call returns; the body call insets the engine's rect by one device pixel on every side so neighbours separate into a brick wall, then fills with the semantic bodyColor. It's a raw canvas — it could just as easily be an arc, a gradient, or a sprite.
if (args.element === 'wick') return; // bricks have no wick
 
const x = body.x + horizontalPixelRatio;
const y = body.y + verticalPixelRatio;
const w = Math.max(1, body.width - horizontalPixelRatio * 2);
const h = Math.max(1, body.height - verticalPixelRatio * 2);
 
ctx.fillStyle = bodyColor;
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
BTC/USDRenko · live · custom candle painter
012345678901234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789012345678901234567890123456789
979899100101102103
00:0012:00