Explain what JSX actually is (syntax sugar), how a compiler (Babel/TypeScript) transforms it into plain JavaScript calls, and why React can work without JSX (you can call the element factory directly). Mention the two JSX runtimes (classic createElement vs automatic react/jsx-runtime) and the practical implications (React import, tooling, and why JSX isn’t HTML). Modern JSX transform removes the explicit React import, but tooling must be configured. Test build output and lint rules.
How does JSX get transformed, and why doesn’t React require it?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea
JSX is not something React executes. JSX is syntax that your build tool (Babel or TypeScript) compiles into plain JavaScript function calls that create React elements. React doesn’t require JSX because React only needs the result: a React element tree (plain JS objects) — and you can create those objects without JSX.
Step | What happens | Who does it |
|---|---|---|
1) Parse JSX | JSX is parsed into an AST (like any JS syntax). | Babel / TypeScript compiler |
2) Transform | JSX nodes become function calls (classic or automatic runtime). | Babel plugin / TS JSX transform |
3) Run | Those function calls return React elements (JS objects). | React runtime |
4) Render | React reconciles elements and updates the DOM. | React + ReactDOM |
Two JSX runtimes you’ll see
There are two common outputs for JSX transforms: classic (calls React.createElement) and automatic (calls jsx/jsxs from react/jsx-runtime). Both produce the same kind of React elements.
// Input (JSX)
function App({ user }) {
return (
<main className="page">
<h1>Hello, {user.name}</h1>
<button onClick={() => alert('hi')}>Click</button>
</main>
);
}
// Output (classic runtime - conceptual)
function App({ user }) {
return React.createElement(
'main',
{ className: 'page' },
React.createElement('h1', null, 'Hello, ', user.name),
React.createElement('button', { onClick: () => alert('hi') }, 'Click')
);
}
// Output (automatic runtime - conceptual)
import { jsx, jsxs } from 'react/jsx-runtime';
function App({ user }) {
return jsxs('main', {
className: 'page',
children: [
jsxs('h1', { children: ['Hello, ', user.name] }),
jsx('button', { onClick: () => alert('hi'), children: 'Click' })
]
});
}
Detail | What it means in practice |
|---|---|
Automatic runtime often removes the need to import React just for JSX | You might not see |
JSX becomes props + children | Attributes become a props object; nested content becomes |
JSX is JavaScript expressions | {...} is an expression slot, not “template interpolation” like HTML. |
Lowercase vs Uppercase matters |
|
Why React doesn’t require JSX
React only needs you to produce React elements. JSX is just a nicer way to write those element factory calls. You can skip JSX entirely and write createElement (or jsx) calls yourself — it’s just more verbose.
// React without JSX
import React from 'react';
export function App() {
return React.createElement('h1', null, 'No JSX here');
}
Interview framing
Say: “JSX is syntax sugar compiled by Babel/TS into element-creation calls (classic createElement or automatic jsx/jsxs). React doesn’t require JSX because it only consumes React elements — JSX never reaches React as JSX.”
Practical scenario
You upgrade to React 17+ and remove import React from files using JSX.
Common pitfalls
- Tooling not configured for the new JSX transform.
- Babel/TS settings inconsistent across packages.
- Lint rules still expecting React in scope.
New transform reduces boilerplate but needs config. Test build output and update linters.