JavaScript XML
JSX (sometimes referred to as JavaScript XML) is an XML-like extension to the JavaScript language syntax.[1] Initially created by Facebook for use with React, JSX has been adopted by multiple web frameworks.[2]: 5 [3]: 11 Being syntactic sugar, JSX is generally transpiled into nested JavaScript function calls structurally similar to the original JSX.
When used with TypeScript, the file extension is .tsx.[4]
Process
[edit]Code written in JSX requires conversion with a tool such as Babel before it can be understood by web browsers.[5][6]: 5 This processing is generally performed during a software build process before the application is deployed.
The transpiler (such as Babel) must make assumptions about the nature of the JSX use, otherwise it would not know what to convert JSX tags into. In the case of Babel (below), we use it in a mode which assumes that the tags are to be used with react, so it converts JSX tags into function calls to the React JSX Runtime, which return values corresponding to the internal representation of these tags.[7] @babel/plugin-transform-react-jsx, which Babel uses for JSX, provides some flexibility for adapting to other platforms by allowing specifying alternative pragmas that take the role of React.createElement and React.Fragment.[7]
Markup
[edit]The following examples include both the React-JSX/TSX code and the transpiled output from Babel.
Basic example
[edit]| TSX Code | Transpiled output (Babel) |
|---|---|
import React from 'react';
const App: React.FC = () => {
return (
<div>
<p>Header</p>
<p>Content</p>
<p>Footer</p>
</div>
);
}
export default App;
|
import React from 'react';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const App = () => {
return /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("p", {
children: "Header"
}),/*#__PURE__*/_jsx("p", {
children: "Content"
}), /*#__PURE__*/_jsx("p", {
children: "Footer"
})]
});
};
export default App;
|
The /*#__PURE__*/ comment added by Babel indicates that the tagged call is to a pure function, which is relevant for later steps such as tree shaking.[8] Because it is not necessary for the functioning of the code, following examples have this annotation removed for ease of reading.
The above transpiler output indicates an output from React 17+. In older versions the output would instead be a more straightforward:
const App = () => {
return React.createElement("div", null,
React.createElement("p", null, "Header"),
React.createElement("p", null, "Content"),
React.createElement("p", null, "Footer"));
};
export default App;
Nested elements
[edit]Multiple elements on the same level need to be wrapped in a single element such as the <div> element shown above, a fragment delineated by <Fragment> or in its shorthand form <>, or returned as an array.[9][10][3]: 68–69
Attributes
[edit]JSX provides a range of element attributes designed to mirror those provided by HTML. Custom attributes can also be passed to the component.[11] All attributes will be received by the component as props.
Custom components
[edit]Custom "tags" (components) are simply JavaScript functions that accept a specific calling style.
| JSX Code | Transpiled output (Babel) |
|---|---|
class Hello extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
function Jello({ name }) {
return <h1>Jello, {name}!</h1>;
}
export default function App() {
return (
<>
<Hello name="Sarah" />
<Jello name="Connor" />
</>
);
}
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
class Hello extends Component {
render() {
return _jsxs("h1", {
children: ["Hello, ", this.props.name, "!"]
});
}
}
function Jello({
name
}) {
return _jsxs("h1", {
children: ["Jello, ", name, "!"]
});
}
export default function App() {
return _jsxs(_Fragment, {
children: [_jsx(Hello, {
name: "Sarah"
}), _jsx(Jello, {
name: "Connor"
})]
});
}
|
Here name="Connor" simply becomes an option { name: "Connor" }.
Components that contain wrap components can be defined using the children attribute that we've already seen in the transpiled output:
| JSX Code | Transpiled output (Babel) |
|---|---|
const Wrapper = (props) => {
return (
<div style={{ backgroundColor: 'lightgray', padding: '20px', border: '1px solid black' }}>
<h2>Inside the Wrapper:</h2>
{props.children}
</div>
);
};
<Wrapper>
<p>Paragraph</p>
<p>Another one!</p>
</Wrapper>
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const Wrapper = props => {
return _jsxs("div", {
style: {
backgroundColor: 'lightgray',
padding: '20px',
border: '1px solid black'
},
children: [_jsx("h2", {
children: "Inside the Wrapper:"
}), props.children]
});
};
_jsxs(Wrapper, {
children: [_jsx("p", {
children: "Paragraph"
}), _jsx("p", {
children: "Another one!"
})]
});
|
JavaScript expressions
[edit]JavaScript expressions (but not statements) can be used inside JSX with curly brackets {}:[3]: 14–16
| JSX Code | Transpiled output (Babel) |
|---|---|
<h1>{10 + 1}</h1>
|
import { jsx as _jsx } from "react/jsx-runtime";
_jsx("h1", {
children: 10 + 1
});
|
The example above will render:
<h1>11</h1>
Conditional expressions
[edit]If–else statements cannot be used inside JSX but conditional expressions can be used instead.
The example below will render { i === 1 ? 'true' : 'false' } as the string 'true' because i is equal to 1.
| TSX Code | Transpiled output (Babel) |
|---|---|
import React from 'react';
const App: React.FC = () => {
const i: number = 1;
return (
<div>
<h1>{ i === 1 ? 'true' : 'false' }</h1>
</div>
);
}
export default App;
|
import React from 'react';
import { jsx as _jsx } from "react/jsx-runtime";
const App = () => {
const i = 1;
return _jsx("div", {
children: _jsx("h1", {
children: i === 1 ? 'true' : 'false'
})
});
};
export default App;
|
The above will render:
<div>
<h1>true</h1>
</div>
Functions and JSX can be used in conditionals:[3]: 88–90
| TSX Code | Transpiled output (Babel) |
|---|---|
import React from 'react';
const App: React.FC = () => {
const sections: number[] = [1, 2, 3];
return (
<div>
{sections.map((n, i) => (
/* 'key' is a React-specific attribute for tracking of list items and their changes */
/* Each 'key' must be unique */
<div key={"section-" + n}>
Section {n} {i === 0 && <span>(first)</span>}
</div>
))}
</div>
);
}
export default App;
|
import React from 'react';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const App = () => {
const sections = [1, 2, 3];
return _jsx("div", {
children: sections.map((n, i) =>
/* 'key' is a React-specific attribute for tracking of list items and their changes */
/* Each 'key' must be unique */
_jsxs("div", {
children: ["Section ", n, " ", i === 0 && _jsx("span", {
children: "(first)"
})]
}, "section-" + n))
});
};
export default App;
|
The above will render:
<div>
<div>Section 1 <span>(first)</span></div>
<div>Section 2</div>
<div>Section 3</div>
</div>
Babel: pragmas
[edit]Transpilers such as Babel ofter the ability to target function calls other than those from React. Here Babel is directed to call tag and frag instead:
| TSX Code | Transpiled output (Babel) |
|---|---|
// @jsx tag
// @jsxfrag frag
const App = () => {
const sections = [1, 2, 3];
return (
<div>
{sections.map((n, i) => (
<div key={"section-" + n}>
Section {n} {i === 0 && <span>(first)</span>}
</div>
))}
</div>
);
};
console.log(JSON.stringify(App()))
|
// @jsx tag
// @jsxfrag frag
const App = () => {
const sections = [1, 2, 3];
return tag(
"div",
null,
sections.map((n, i) =>
tag("div", { key: "section-" + n }, "Section ", n, " ",
i === 0 && tag("span", null, "(first)")),
),
);
};
|
With different versions of tag and frag, different representations of the input can be produced:
| JS Code | Program output |
|---|---|
// Object tree.
// Fragments are represented as arrays
function frag(...children) {
return children;
}
// Tags are represented as objects
function tag(name, attrs, ...children) {
// Flattening the array unpacks the fragments
// Still need to deal with "false"
const kids = []
for (x of children.flat(Infinity)) {
if (x === false || x === null || x === undefined) {
if (kids.at(-1) === " ") kids.pop()
}
else kids.push(x)
}
return {name, children: kids, ...(attrs && { attrs })}
}
console.log(JSON.stringify(App()));
|
{
"name": "div",
"children": [
{
"name": "div",
"attrs": { "key": "section-1" },
"children": [
"Section ", 1, " ",
{ "name": "span", "children": ["(first)"] }
]
},
{
"name": "div",
"attrs": { "key": "section-2" },
"children": ["Section ", 2]
},
{
"name": "div",
"attrs": { "key": "section-3" },
"children": ["Section ", 3]
}
]
}
|
// Pug-like output
function frag(...children) {
let kids = [];
for (const x of children) {
// Text start with |
if (!Array.isArray(x)) kids.push("| " + x);
else kids.push(...x.flat(Infinity));
}
}
function tag(name, attrs, ...children) {
const indent = " ";
const attrStr = Object.entries(attrs || {})
.map(([k, v]) => `${k}="${v}"`)
.join(", ");
const head = `${name}${attrStr ? `(${attrStr})` : ""}`;
let kids = [];
for (const x of frag(children)) {
if (x === false) {
if (kids.at(-1) === "| ") kids.pop();
} else kids.push(x);
}
if (kids.length == 1)
// Shorthand syntax for elem>text and for element nesting
return kids[0].startsWith("| ")
? [`${head} ${kids[0].substr(2)}`]
: [`${head}: ${kids[0]}`];
return [head, ...kids.map((line) => indent + line)];
}
console.log(App().join('\n'));
|
div
div(key="section-1")
| Section
| 1
|
span (first)
div(key="section-2")
| Section
| 2
div(key="section-3")
| Section
| 3
|
See also
[edit]References
[edit]- ^ "Draft: JSX Specification". JSX. Facebook. Retrieved 7 April 2018.
- ^ Larsen, John (2021). React Hooks in Action With Suspense and Concurrent Mode. Manning. ISBN 978-1720043997.
- ^ a b c d Wieruch, Robin (14 September 2018). The Road to React. Leanpub. ISBN 978-1720043997.
- ^ "Documentation - JSX". www.typescriptlang.org. Retrieved 2025-07-13.
- ^ Fischer, Ludovico (2017-09-06). React for Real: Front-End Code, Untangled. Pragmatic Bookshelf. ISBN 9781680504484.
- ^ Larsen, John (2021). React Hooks in Action With Suspense and Concurrent Mode. Manning. ISBN 978-1720043997.
- ^ a b "@babel/plugin-transform-react-jsx · Babel". babeljs.io.
- ^ "compiler-notations-spec/pure-notation-spec.md at main · javascript-compiler-hints/compiler-notations-spec". GitHub.
- ^ Clark, Andrew (September 26, 2017). "React v16.0§New render return types: fragments and strings". React Blog.
- ^ "React.Component: render". React.
- ^ Clark, Andrew (September 26, 2017). "React v16.0§Support for custom DOM attributes". React Blog.
External links
[edit]- Official website, Draft: JSX specification