remotion-dev/remotion · error
Could not find a root JSX element
Error message
Could not find a root JSX element
What it means
appendElementToRoot is a recast-based codemod helper in @remotion/studio-codemods that appends a JSX element to a component's returned root. It expects the enclosing return statement's argument to be a JSXFragment or JSXElement (the root JSX). If the argument is something else (null, a call expression like createElement(), a conditional, etc.) it throws 'Could not find a root JSX element'.
Source
Thrown at packages/studio-codemods/src/recast-mods.ts:471
}) => {
folderElement.openingElement.selfClosing = false;
folderElement.closingElement = folderElement.closingElement ?? {
type: 'JSXClosingElement',
name: folderElement.openingElement.name,
};
folderElement.children.push(stripParenthesizedExtra(element));
};
const appendElementToRoot = ({
element,
returnStatement,
}: {
element: JSXElement;
returnStatement: ReturnStatement;
}) => {
const {argument} = returnStatement;
if (argument?.type !== 'JSXFragment' && argument?.type !== 'JSXElement') {
throw new Error('Could not find a root JSX element');
}
if (argument.type === 'JSXFragment') {
(argument.children as JSXFragment['children']).push(
stripParenthesizedExtra(element),
);
} else {
returnStatement.argument = wrapInJsxFragment([
argument as unknown as JSXElement,
element,
]) as never;
}
};
const getEnclosingReturnStatement = (path: recast.types.NodePath) => {
let currentPath: recast.types.NodePath | null = path;
while (currentPath !== null) {
if (View on GitHub (pinned to a6a7485a9a)
Solutions
- Rewrite the Root component to return a literal JSX element or fragment: return (<>...</>)
- Remove or hoist early non-JSX returns (e.g. `return null`) so the enclosing return statement yields JSX
- If the root is wrapped in a conditional, extract the conditional inside a fragment: return (<>{cond ? <A/> : <B/>}</>)
- Run the codemod on a file that directly contains the JSX Root rather than a re-export module
Example fix
// before
export const RemotionRoot = () => {
if (!videos.length) return null;
return <>{videos.map(...)}</>;
};
// after
export const RemotionRoot = () => {
return (<>{videos.length ? videos.map(...) : null}</>);
}; Defensive patterns
Strategy: validation
Validate before calling
const returnsJsx = /return\s*\(?\s*<[A-Za-z>]/.test(componentSource);
if (!returnsJsx) throw new Error('Root must return JSX for codemods'); Type guard
const isJsxReturn = (arg: unknown): arg is JSXElement | JSXFragment =>
arg !== null && typeof arg === 'object' &&
['JSXElement', 'JSXFragment'].includes((arg as {type?: string}).type ?? ''); Try / catch
try {
applyCodemod(file, transformation);
} catch (err) {
if (err instanceof Error && err.message === 'Could not find a root JSX element') {
console.warn('Root file does not return JSX directly; skipping codemod');
return {applied: false};
}
throw err;
} Prevention
- Keep Root components returning literal JSX (element or fragment)
- Avoid early `return null` before the JSX return
- Prefer JSX over React.createElement in Root files
- Warn users when their Root file structure is not codemod-compatible
When it happens
Trigger: Running a move-composition/move-to-folder codemod against a Root file whose component returns a non-JSX root — e.g. return null, return createComponent(...), a conditional expression, or a return statement whose argument is missing.
Common situations: Root files written with React.createElement instead of JSX; early `return null;` before the JSX return; commented-out or generated Root files without a JSX return; files that re-export the Root from another module.
Related errors
- Could not find ${getCompositionOrFolderLabel(transformation.
- Cannot split a JSX sequence without a source location
- Cannot split JSX sequence with no parent
- Could not find ${getCompositionOrFolderLabel(transformation.
- Could not find folder "${folderPath}"
AI-assisted analysis of remotion-dev/remotion@a6a7485a9a (2026-09-02).
Data as JSON: /api/errors/2a6a6054a4c6cfe8.
Report an issue: GitHub.