shadcn-ui/ui · error · Error
Failed to transform JSX
Error message
Failed to transform JSX
What it means
Thrown by transformJsx when babel's transformFromAstSync returns no result or a result without an .ast property. transformJsx runs only when config.tsx is false (JS project): it parses the source with babel, then strips TypeScript types via the transformTypescript plugin to emit JS. A null result means babel could not produce an AST, usually due to unsupported/invalid syntax.
Source
Thrown at packages/shadcn/src/utils/transformers/transform-jsx.ts:91
const ast = recast.parse(output, {
parser: {
parse: (code: string) => {
return parse(code, PARSE_OPTIONS)
},
},
})
const result = transformFromAstSync(ast, output, {
cloneInputAst: false,
code: false,
ast: true,
plugins: [transformTypescript],
configFile: false,
})
if (!result || !result.ast) {
throw new Error("Failed to transform JSX")
}
return recast.print(result.ast).code
}
View on GitHub (pinned to efac598707)
Solutions
- Set "tsx": true in components.json if your project is TypeScript — this skips the JS transform entirely.
- If you must stay on JS, simplify the offending registry file's syntax to widely-supported JS/JSX.
- Ensure @babel/core, @babel/preset-typescript, and recast are installed at compatible versions.
- Check the file content for truncation or encoding issues, then retry.
Example fix
// components.json (before) — JS project forces transform
{ "tsx": false }
// after — if project is TS, enable tsx to skip transform
{ "tsx": true } Defensive patterns
Strategy: fallback
Validate before calling
// before installing into a JS project, sanity-parse each file
import { parse } from "@babel/parser"
try {
parse(file.content, { sourceType: "module", plugins: ["jsx", "typescript"] })
} catch (e) {
throw new Error(`file ${file.path} cannot be parsed for JS transform`)
} Type guard
const isTransformableJsx = (source: string): boolean => {
try { parse(source, PARSE_OPTIONS); return true } catch { return false }
} Try / catch
try {
await transformJsx({ sourceFile, config })
} catch (e) {
if (e instanceof Error && /Failed to transform JSX/.test(e.message)) {
// fall back to keeping the file as-is or skip the JS transform
}
throw e
} Prevention
- Prefer "tsx": true in components.json for TypeScript projects to skip this transform.
- Restrict registry file syntax to stable JS/JSX/TS supported by the bundled babel.
- Keep @babel/core and @babel/preset-typescript versions aligned with shadcn.
- Pre-validate registry files with the same babel parser options.
When it happens
Trigger: Adding a registry file to a non-TSX (JS) project; transformJsx parses file.content with babel parser (PARSE_OPTIONS including TypeScript + JSX + many plugins), runs transformFromAstSync with transformTypescript, and checks `if (!result || !result.ast)`.
Common situations: Registry file uses syntax babel's configured plugins cannot parse (experimental TC39 proposals, decorators stage mismatch, very new TS syntax); corrupted/truncated file content; babel/core version mismatch in the consumer's node_modules.
Related errors
- Missing cached source for ${base.name}/${file.path}
- NOT_FOUND
- Generated styles are missing or stale (${missingStyles.join(
- Unknown ${flag} target "${target}". Valid targets: ${valid}.
- --style ${target} is not supported because it is a legacy so
AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12).
Data as JSON: /api/errors/710ede2ad06c0883.
Report an issue: GitHub.