{"record":{"id":"250738b144540cf3","repo":"remotion-dev/remotion","slug":"tried-to-call-an-api-that-only-works-in-the-browse","errorCode":null,"errorMessage":"Tried to call an API that only works in the browser from outside the browser","messagePattern":"Tried to call an API that only works in the browser from outside the browser","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/portal-node.ts","lineNumber":31,"sourceCode":"\t\t);\n\t};\n};\n\nexport const setPortalNodeCurrentScale = (scale: number) => {\n\tif (portalNodeCurrentScale === scale) {\n\t\treturn;\n\t}\n\n\tportalNodeCurrentScale = scale;\n\tfor (const listener of portalNodeCurrentScaleListeners) {\n\t\tlistener();\n\t}\n};\n\nexport const portalNode = () => {\n\tif (!_portalNode) {\n\t\tif (typeof document === 'undefined') {\n\t\t\tthrow new Error(\n\t\t\t\t'Tried to call an API that only works in the browser from outside the browser',\n\t\t\t);\n\t\t}\n\n\t\t_portalNode = document.createElement('div');\n\t\t_portalNode.style.position = 'absolute';\n\t\t_portalNode.style.top = '0px';\n\t\t_portalNode.style.left = '0px';\n\t\t_portalNode.style.right = '0px';\n\t\t_portalNode.style.bottom = '0px';\n\t\t_portalNode.style.width = '100%';\n\t\t_portalNode.style.height = '100%';\n\t\t_portalNode.style.display = 'flex';\n\t\t_portalNode.style.flexDirection = 'column';\n\n\t\tconst containerNode = document.createElement('div');\n\t\tcontainerNode.style.position = 'fixed';\n\t\tcontainerNode.style.top = -999999 + 'px';","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/core/src/portal-node.ts#L13-L49","documentation":"portalNode() lazily creates a DOM <div> (positioned absolutely to fill the frame) used to host portaled content during rendering. It checks `typeof document === 'undefined'` and throws because the portal mechanism fundamentally requires a real DOM. Calling it in Node/SSR/serverless contexts without a DOM fails fast instead of crashing deeper in React.","triggerScenarios":"Importing or invoking portalNode() during SSR, inside a React server component, in a Node script, or in a headless render environment where no DOM is attached.","commonSituations":"Using Remotion browser-only APIs inside a Next.js server component; running composition code in a Node test without jsdom; calling the API at module top-level in an isomorphic bundle.","solutions":["Only call browser-only APIs inside components that render in the browser or Remotion Studio.","In jsdom-based tests, ensure the test environment exposes `document`.","Gate the call: if (typeof document !== 'undefined') { ... }."],"exampleFix":"// before\nconst node = portalNode();\n// after\nif (typeof document !== 'undefined') {\n  const node = portalNode();\n}","handlingStrategy":"type-guard","validationCode":"if (typeof document !== 'undefined') {\n  const node = portalNode();\n}","typeGuard":"const hasDOM = (): boolean => typeof document !== 'undefined';","tryCatchPattern":null,"preventionTips":["Keep browser-only calls inside components that render in the browser/Studio.","Avoid calling browser APIs at module top-level in isomorphic bundles.","Configure jsdom in Node test environments that exercise DOM code."],"tags":["ssr","browser-only","dom","portal"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}