What Is JSX and Why Is It Used in React?
Advertisement
🧩 Scenario
Architecture Walkthrough
JSX as Syntactic Sugar
JSX is not a language. It is a syntax extension that compiles to JavaScript function calls. Every JSX element is transformed by Babel (or the React compiler) into a call to React.createElement(type, props, ...children). The type argument is either a string for HTML elements or a reference to a component function for custom components. The props argument is an object of the element's attributes. Children are the remaining arguments.
This compilation step explains several JSX rules. Component names must start with an uppercase letter because JSX uses the case to determine whether type should be a string (lowercase, treated as an HTML element) or a variable reference (uppercase, treated as a React component). A lowercase <button> becomes React.createElement('button', ...). An uppercase <Button> becomes React.createElement(Button, ...) where Button is looked up in scope.
Expressions, Not Statements
Inside JSX curly braces, you can embed any JavaScript expression: variables, function calls, ternary operators, logical &&, template literals. You cannot embed statements such as if, for, or while. This is because the curly braces resolve to a value that becomes an argument to React.createElement. An if statement does not produce a value and cannot be used in that position.
For conditional rendering, use the ternary operator (condition ? <A /> : <B />) or short-circuit evaluation (condition && <A />). For lists, use array.map() which returns an expression (an array of elements).
The New JSX Transform
Before React 17, the Babel transform used React.createElement, which required React to be in scope in every file that used JSX. React 17 introduced a new JSX transform that imports a different internal function (jsx from react/jsx-runtime) automatically, removing the need to import React from 'react' in every component file.
Key Code Explained
// JSX
const element = (
<button className="primary" onClick={handleClick} disabled={isLoading}>
{isLoading ? 'Saving...' : 'Save'}
</button>
);
// What Babel transforms it to (old transform):
const element = React.createElement(
'button',
{
className: 'primary', // note: className not class
onClick: handleClick,
disabled: isLoading,
},
isLoading ? 'Saving...' : 'Save', // children
);
// New JSX transform (React 17+):
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('button', {
className: 'primary',
onClick: handleClick,
disabled: isLoading,
children: isLoading ? 'Saving...' : 'Save',
});
// Why className, not class?
// 'class' is a reserved keyword in JavaScript.
// Since JSX is compiled to JS objects, the property name cannot be 'class'.
// React chose className to mirror the DOM property name (element.className).
// Similarly, 'for' becomes 'htmlFor' on <label>.
// Conditional rendering patterns inside JSX
function StatusBadge({ status, count }: { status: string; count: number }) {
return (
<div>
{/* Ternary: for if/else */}
<span>{status === 'active' ? 'Active' : 'Inactive'}</span>
{/* Short-circuit &&: for "render if true" */}
{count > 0 && <span className="badge">{count}</span>}
{/* Mapping a list: returns an array of elements */}
{['a', 'b', 'c'].map((tag) => (
<span key={tag} className="tag">
{tag}
</span>
))}
</div>
);
}
// Self-closing tags: required for elements with no children
// HTML: <input type="text">
// JSX: <input type="text" /> (trailing slash required for void elements)
// Fragments: render multiple siblings without a wrapper DOM node
function MultiReturn() {
return (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
}
// Compiles to: React.createElement(React.Fragment, null, h1_element, p_element)
The {count > 0 && <span>...</span>} pattern has a gotcha: if count is 0, the expression evaluates to 0 which React renders as the text "0". To avoid this, use a boolean: {count > 0 && <span>...</span>} already produces a boolean on the left side (true or false), but if count is the direct left operand, use {!!count && <span>...</span>} or {count > 0 && ...} explicitly.
Tradeoffs
| Aspect | JSX | React.createElement directly |
|---|---|---|
| Readability | High (resembles the output structure) | Low (deeply nested function calls) |
| Learning curve | Requires knowing JSX rules | Pure JavaScript, no new syntax |
| Tooling support | Excellent (linters, formatters, TS) | Works but no IDE-level JSX tooling |
| Build step required | Yes | No (but rarely done in practice) |
What Interviewers Actually Check
- Whether you know JSX compiles to
React.createElementand what that means for the rules - Whether you can explain why component names must be uppercase
- Whether you can explain three JSX attribute differences from HTML and why they exist
- Whether you know why
ifstatements cannot go inside curly braces - Whether you know the new JSX transform and why it removes the React import requirement
Follow-Up Questions
- Can you write React components without JSX at all? What are the downsides?
- How does TypeScript type-check JSX? What is
JSX.ElementvsReact.ReactNodevsReact.ReactElement? - What is the pragma comment
/** @jsx ... */and when would you use it? - What does the
React.StrictModewrapper do that is specific to development mode? - How does JSX handle whitespace between elements compared to HTML?
Common Candidate Mistakes
- Saying JSX is HTML, not knowing it is a JavaScript expression that compiles to function calls
- Not knowing that the build step is required and wondering why JSX does not work natively in the browser
- Writing
class=instead ofclassName=and not knowing why it must be different - Trying to write
{ if (show) { return <div /> } }inside JSX and not understanding why it fails - Not knowing that falsy values like
0render as text in JSX ({count && ...}where count is 0 renders "0")
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you explain what JSX compiles to and show a before/after example?
- Can you list three JSX attribute differences from HTML and explain why each exists?
- Can you explain why if/else statements do not work inside curly braces and what to use instead?
- Can you write a JSX expression with conditional rendering, event handlers, and a mapped list?
- Can you explain why the new JSX transform removes the need to import React in every file?
Summary
JSX is syntactic sugar that transforms human-readable markup into React.createElement function calls. Each JSX element compiles to React.createElement(type, props, ...children). This compilation happens at build time via Babel or the React compiler; browsers never execute JSX directly.
The compilation to function calls is the root cause of several JSX rules. Component names must be uppercase so the JSX transform uses a variable reference rather than a string. Attribute names use camelCase and differ from HTML attributes (className, htmlFor) because JSX is JavaScript and cannot use reserved keywords as property names. Only expressions are valid inside curly braces because the curly-brace content becomes a function argument.
JSX exists as a developer experience choice: it makes the component output structure visible in the source code, with logic and markup collocated rather than separated into templates and script files. The tradeoff is a required build step, but every React toolchain (Create React App, Vite, Next.js) configures this automatically.
Is JSX valid JavaScript?
No. JSX is a syntax extension to JavaScript. Browsers cannot execute JSX directly. Babel or the React compiler transforms JSX into React.createElement calls before the code runs in the browser.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement