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

  1. Set "tsx": true in components.json if your project is TypeScript — this skips the JS transform entirely.
  2. If you must stay on JS, simplify the offending registry file's syntax to widely-supported JS/JSX.
  3. Ensure @babel/core, @babel/preset-typescript, and recast are installed at compatible versions.
  4. 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

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


AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12). Data as JSON: /api/errors/710ede2ad06c0883. Report an issue: GitHub.