What is JSX and how does it work?
TL;DR
JSX is a syntax extension for JavaScript that lets you describe UI with HTML-like markup inside JavaScript. A build tool transforms it into ordinary function calls before the browser runs it. With the modern automatic runtime, <div>Hello, world!</div> becomes a call to a helper from react/jsx-runtime; the older classic transform used React.createElement.
What is JSX and how does it work?
JSX is syntax that a build tool transforms into calls producing React element descriptions; browsers do not execute JSX directly.
What is JSX?
JSX is a syntax extension for JavaScript that lets you describe UI trees with an HTML-like syntax. Although it was popularized by React, JSX itself is a separate spec and is also used by other libraries such as Preact and Solid. TypeScript supports it natively in .tsx files.
How does JSX work?
JSX is not valid JavaScript by itself. A compiler — typically Babel or the bundler's built-in transform (SWC, esbuild, Oxc) — converts JSX into ordinary JavaScript function calls before the browser sees it.
JSX syntax
JSX allows you to write HTML-like tags directly in your JavaScript code. For example:
const element = <h1>Hello, world!</h1>;
Transformation process
Since React 17 (2020), the default transform is the automatic JSX runtime. Instead of compiling to React.createElement, the compiler imports jsx / jsxs helpers from react/jsx-runtime and emits calls to those. A consequence is that you no longer need to import React from 'react' just to use JSX:
// Sourceconst element = <h1>Hello, world!</h1>;
// Output with the automatic runtime (conceptually)import { jsx as _jsx } from 'react/jsx-runtime';const element = _jsx('h1', { children: 'Hello, world!' });
The older "classic" transform compiled the same JSX to React.createElement('h1', null, 'Hello, world!') and required React to be in scope. The classic form is still useful as a mental model for what JSX desugars to, but new projects should use the automatic runtime.
Regardless of which transform is configured, JSX becomes ordinary JavaScript before React receives an element description:
Embedding expressions
You can embed JavaScript expressions inside JSX using curly braces {}. For example:
const name = 'John';const element = <h1>Hello, {name}!</h1>;
Attributes in JSX
You can use quotes to specify string literals as attributes and curly braces to embed JavaScript expressions. For example:
const element = <img src={user.avatarUrl} alt="User Avatar" />;
Because JSX attributes compile to JavaScript object keys, a few HTML attribute names are renamed to avoid clashing with reserved words or to follow JS camelCase conventions:
classbecomesclassNameforbecomeshtmlFor- Event handlers are camelCased:
onclickbecomesonClick,onchangebecomesonChange - Most other DOM properties (
tabIndex,readOnly,maxLength, etc.) use camelCase
Fragments
To return multiple elements without an extra wrapper DOM node, use a fragment. The shorthand syntax is <>...</>:
function List() {return (<><li>One</li><li>Two</li></>);}
The longer form <Fragment key={id}>...</Fragment> is needed when you must pass a key.
JSX is an expression
After compilation, JSX expressions become regular JavaScript function calls and evaluate to JavaScript objects. This means you can use JSX inside if statements, assign it to variables, and pass it as a prop or argument.
JSX prevents injection attacks
By default, React DOM escapes any values embedded in JSX with {} before rendering them, which neutralizes the most common XSS vector — injecting markup via untrusted strings:
const userInput = '<img src=x onerror="alert(1)" />';const safe = <div>{userInput}</div>; // rendered as text, not as HTML
This protection is not absolute. Two notable escape hatches still bypass escaping and can introduce XSS if fed untrusted data:
dangerouslySetInnerHTML={{ __html: ... }}injects raw HTML into the DOM.- React 19 blocks
javascript:URLs in URL-valued attributes such ashrefandsrc. You should still validate attacker-controlled URLs against the schemes and destinations your application permits because other schemes, redirects, and resource types can carry their own risks.
Further reading
- React: Writing markup with JSX
- React: Introducing the new JSX transform
- React 19 upgrade guide:
javascript:URLs - Babel