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 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