remotion-dev/remotion · error · Error
measureText() can only be called in a browser.
Error message
measureText() can only be called in a browser.
What it means
measureText() in @remotion/layout-utils creates a DOM span and calls getBoundingClientRect / getComputedStyle, so it fundamentally requires a browser. It throws this if `typeof document === 'undefined'`, i.e. when the call happens in Node.js, Bun, or any non-DOM runtime.
Source
Thrown at packages/layout-utils/src/layouts/measure-text.ts:61
} & CSSPropertiesOnWord;
const wordCache = new Map<string, Dimensions>();
const takeMeasurement = ({
text,
fontFamily,
fontSize,
fontWeight,
letterSpacing,
fontVariantNumeric,
additionalStyles,
textTransform,
}: Omit<Word, 'fontFamily'> & {fontFamily: string | null}): {
boundingBox: DOMRect;
computedFontFamily: string;
} => {
if (typeof document === 'undefined') {
throw new Error('measureText() can only be called in a browser.');
}
const node = document.createElement('span');
if (fontFamily) {
node.style.fontFamily = fontFamily;
}
node.style.display = 'inline-block';
node.style.position = 'absolute';
node.style.top = `-10000px`;
node.style.whiteSpace = 'pre';
node.style.fontSize =
typeof fontSize === 'string' ? fontSize : `${fontSize}px`;
if (additionalStyles) {
for (const key of Object.keys(
additionalStyles,View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Move the measureText() call into a component body or hook that only executes during Remotion rendering (browser context).
- Configure your test runner with a DOM environment (jsdom/happy-dom) if the function must run in tests.
- Avoid importing layout-utils into server-only code paths.
- Gate the call: `if (typeof document !== 'undefined') { measureText(...) }` when shared between server and client.
Example fix
// before
// module scope, runs in Node
const widths = words.map((w) => measureText({text: w, fontFamily: 'Inter', fontSize: 16}));
// after
import {useEffect} from 'react';
function MyComp({words}) {
const [widths, setWidths] = useState<Record<string, Dimensions>>({});
useEffect(() => {
const next: Record<string, Dimensions> = {};
for (const w of words) next[w] = measureText({text: w, fontFamily: 'Inter', fontSize: 16});
setWidths(next);
}, [words]);
return null;
} Defensive patterns
Strategy: type-guard
Validate before calling
const canMeasure = typeof document !== 'undefined';
if (!canMeasure) {
// skip measurement or precompute from a fixture
} Type guard
export const isBrowser = (): boolean => typeof document !== 'undefined';
Try / catch
try {
const dims = measureText({text, fontFamily: 'Inter', fontSize: 16});
} catch (e) {
if ((e as Error).message.includes('browser')) {
// SSR/test path: return fallback dims
} else throw e;
} Prevention
- Never call measureText() at module scope; call it inside component bodies or effects.
- Configure test runners with jsdom/happy-dom when measuring in tests.
- Use typeof document !== 'undefined' to branch shared code.
- Keep layout-utils imports out of server-only modules.
When it happens
Trigger: Calling measureText() during SSR, in a Node test (jest/vitest without jsdom), in a server component, or in a top-level module-evaluation context that runs outside the Remotion browser render.
Common situations: Importing a layout utility that calls measureText() into a Node-side script (e.g. to precompute widths); running component tests without a DOM environment; calling measureText() at module scope rather than inside a component body that only runs in the browser.
Related errors
- Tried to call an API that only works in the browser from out
- Called measureText() with "fontFamily": ${JSON.stringify(fon
- getAudioData() is only available in the browser.
- getAudioDuration() is only available in the browser.
- getImageDimensions() is only available in the browser.
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/16a2c804311b5007.
Report an issue: GitHub.