remotion-dev/remotion · error · Error
HtmlInCanvas: `width` and `height` must be numbers. Received
Error message
HtmlInCanvas: `width` and `height` must be numbers. Received width=${String(width)}, height=${String(height)}. What it means
Thrown by assertHtmlInCanvasDimensions (a private validator inside <HtmlInCanvas>) before any render work, when `width` or `height` is not of type 'number'. <HtmlInCanvas> paints DOM into a <canvas> of exact pixel dimensions, so it cannot accept strings (like '100%') or other types the way a CSS-sized div would.
Source
Thrown at packages/core/src/HtmlInCanvas.tsx:251
return Number(match[1]);
};
export type HtmlInCanvasOnPaint = (
params: HtmlInCanvasOnPaintParams,
) => void | Promise<void>;
export type HtmlInCanvasOnInitCleanup = () => void;
export type HtmlInCanvasOnInit = (
params: HtmlInCanvasOnPaintParams,
) => HtmlInCanvasOnInitCleanup | Promise<HtmlInCanvasOnInitCleanup>;
export type HtmlInCanvasPixelDensity = number;
function assertHtmlInCanvasDimensions(width: unknown, height: unknown): void {
if (typeof width !== 'number' || typeof height !== 'number') {
throw new Error(
`HtmlInCanvas: \`width\` and \`height\` must be numbers. Received width=${String(width)}, height=${String(height)}.`,
);
}
if (!Number.isInteger(width) || width <= 0) {
throw new Error(
`HtmlInCanvas: \`width\` must be a positive integer. Received: ${String(width)}.`,
);
}
if (!Number.isInteger(height) || height <= 0) {
throw new Error(
`HtmlInCanvas: \`height\` must be a positive integer. Received: ${String(height)}.`,
);
}
}
function resolveHtmlInCanvasPixelDensity(View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Pass explicit numeric width/height props, e.g. `width={1920} height={1080}`.
- If pulling dimensions from a ref/getComputedStyle, parse with `Number(el.clientWidth)` or `parseInt(value, 10)`.
- Avoid template literals or percentage strings for width/height on <HtmlInCanvas>; reserve CSS sizing for `style`.
- Type the props as `number` in your wrapper component so TypeScript catches this at compile time.
Example fix
// before
<HtmlInCanvas width="100%" height="auto">...</HtmlInCanvas>
// after
<HtmlInCanvas width={1920} height={1080}>...</HtmlInCanvas> Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof width !== 'number' || typeof height !== 'number') {
throw new TypeError(`width/height must be numbers; got ${typeof width}, ${typeof height}`);
}
return <HtmlInCanvas width={width} height={height}>...</HtmlInCanvas>; Type guard
const isNumberPair = (w: unknown, h: unknown): w is number, h is number => typeof w === 'number' && typeof h === 'number'; // (use a single boolean guard in real code)
Prevention
- Type wrapper props so width/height are `number` (not string | number).
- Avoid passing getComputedStyle or CSS values directly to <HtmlInCanvas>.
- Use `parseInt(value, 10)` when bridging CSS strings to canvas dimensions.
- Write a small unit test asserting <HtmlInCanvas> only accepts numeric dims.
When it happens
Trigger: Passing `width="100%"`, `width="1080"` (string), `width={null}`, or omitting width/height on <HtmlInCanvas>; passing width/height derived from a ref measurement that returns a string.
Common situations: Treating <HtmlInCanvas> like a regular CSS layout component; copying responsive CSS values into canvas dimensions; reading dimensions from getComputedStyle (which returns strings) and forwarding them directly.
Related errors
- You passed to durationInFrames an argument of type ${typeof
- You passed to the "from" props of your <Sequence> an argumen
- threshold must be a number, got ${threshold} of type ${typeo
- Emoji ${emoji} not found. Available emojis: ${emojis.map((e)
- No id for composition passed.
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/f9b4a3de335d24bb.
Report an issue: GitHub.