{"record":{"id":"16a2c804311b5007","repo":"remotion-dev/remotion","slug":"measuretext-can-only-be-called-in-a-browser","errorCode":null,"errorMessage":"measureText() can only be called in a browser.","messagePattern":"measureText\\(\\) can only be called in a browser\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/layout-utils/src/layouts/measure-text.ts","lineNumber":61,"sourceCode":"} & CSSPropertiesOnWord;\n\nconst wordCache = new Map<string, Dimensions>();\n\nconst takeMeasurement = ({\n\ttext,\n\tfontFamily,\n\tfontSize,\n\tfontWeight,\n\tletterSpacing,\n\tfontVariantNumeric,\n\tadditionalStyles,\n\ttextTransform,\n}: Omit<Word, 'fontFamily'> & {fontFamily: string | null}): {\n\tboundingBox: DOMRect;\n\tcomputedFontFamily: string;\n} => {\n\tif (typeof document === 'undefined') {\n\t\tthrow new Error('measureText() can only be called in a browser.');\n\t}\n\n\tconst node = document.createElement('span');\n\n\tif (fontFamily) {\n\t\tnode.style.fontFamily = fontFamily;\n\t}\n\n\tnode.style.display = 'inline-block';\n\tnode.style.position = 'absolute';\n\tnode.style.top = `-10000px`;\n\tnode.style.whiteSpace = 'pre';\n\tnode.style.fontSize =\n\t\ttypeof fontSize === 'string' ? fontSize : `${fontSize}px`;\n\n\tif (additionalStyles) {\n\t\tfor (const key of Object.keys(\n\t\t\tadditionalStyles,","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/layout-utils/src/layouts/measure-text.ts#L43-L79","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n// module scope, runs in Node\nconst widths = words.map((w) => measureText({text: w, fontFamily: 'Inter', fontSize: 16}));\n// after\nimport {useEffect} from 'react';\nfunction MyComp({words}) {\n  const [widths, setWidths] = useState<Record<string, Dimensions>>({});\n  useEffect(() => {\n    const next: Record<string, Dimensions> = {};\n    for (const w of words) next[w] = measureText({text: w, fontFamily: 'Inter', fontSize: 16});\n    setWidths(next);\n  }, [words]);\n  return null;\n}","handlingStrategy":"type-guard","validationCode":"const canMeasure = typeof document !== 'undefined';\nif (!canMeasure) {\n  // skip measurement or precompute from a fixture\n}","typeGuard":"export const isBrowser = (): boolean => typeof document !== 'undefined';","tryCatchPattern":"try {\n  const dims = measureText({text, fontFamily: 'Inter', fontSize: 16});\n} catch (e) {\n  if ((e as Error).message.includes('browser')) {\n    // SSR/test path: return fallback dims\n  } else throw e;\n}","preventionTips":["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."],"tags":["typescript","layout-utils","browser-only","dom","ssr"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}