Understanding Babel's Automatic JSX Transform: Configuring and Verifying React Output
Learn how Babel's automatic JSX transform works, configure it with preset-react, verify the output, and understand the bundle‑size trade‑off.
20 May 2026, 08:27 UTC

The problem: JSX doesn't run in browsers
React components are written in JSX, a syntax extension that looks like HTML inside JavaScript. Browsers cannot execute JSX directly; it must be turned into plain JavaScript function calls before the code reaches the client. Babel handles this transformation, but the default behavior changed in Babel 7.4, and many projects still run into mismatched output because the configuration wasn't updated.
How the automatic transform works
Starting with Babel 7.4, @babel/preset-react enables the automatic JSX transform. Instead of importing React and writing React.createElement yourself, the compiler injects a runtime import from @babel/runtime and rewrites each JSX element to a call like jsx('div', { children: 'Hello' }). This removes the need for a top‑level import React from 'react' in every file, but it adds a dependency on @babel/runtime and requires the runtime to be present in the final bundle.
Minimal configuration that works today
Create a babel.config.js at the project root (or add a babel field to package.json). The preset must be told to use the automatic runtime:
module.exports = {
presets: [
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-env"
]
};
Install the required packages (run in the project directory, no elevated permissions needed):
npm install --save-dev @babel/core @babel/cli @babel/preset-react @babel/preset-env @babel/runtime
Worked example: transpiling a component
Assume a file src/Greeting.jsx:
export function Greeting({ name }) {
return Hello, {name}!;
}
Run the CLI from the project root:
npx babel src/Greeting.jsx --out-dir lib
Expected check: open lib/Greeting.js and verify that the output resembles:
import { jsx as _jsx } from "react/jsx-runtime";
export function Greeting({ name }) {
return _jsx("div", { children: ["Hello, ", name, "!"] });
}
If you see React.createElement instead, the preset is still using the classic transform—add runtime: "automatic" or upgrade the preset.
Trade‑off: bundle size vs. developer ergonomics
The automatic transform eliminates repetitive imports, but it pulls in @babel/runtime helpers. In a small library this can add a few kilobytes; in a large app the impact is usually negligible because the runtime is shared across all transformed files. If you need to keep the bundle minimal and control the exact helper version, you can switch back to the classic transform (runtime: "classic") and import React manually.
Common misconfiguration and how to diagnose it
- Missing runtime package: Build fails with "Cannot find module 'react/jsx-runtime'". Fix by installing
@babel/runtime(already listed above) and ensuring your bundler resolvesreact/jsx-runtime. - Wrong pragma: Legacy code may set
/** @jsx React.DOM */or a custom pragma. The automatic transform ignores pragma comments; remove them or setpragmain the preset options if you must keep a custom factory. - Mixed transforms: Some files processed with the classic preset, others with automatic. Run
npx babel --versionandnpm ls @babel/preset-reactto confirm a single version is used project‑wide.
Actionable closing checklist
- Confirm
@babel/preset-reactversion ≥ 7.4 (runnpm ls @babel/preset-react). - Set
runtime: "automatic"in the preset options. - Install
@babel/runtimeas a production dependency (npm install @babel/runtime). - Transpile a test file with
npx babel src/Test.jsx --out-dir liband inspect the output forjsx-runtimeimports. - Run your bundler (Webpack, Vite, Rollup) and verify the final bundle contains no
React.createElementcalls unless you intentionally kept the classic transform.
Following these steps gives you a predictable JSX compilation pipeline that works with modern React tooling and avoids the classic “missing React import” errors.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.