React Basics #3: What Is JSX?
Last time we created our first React project with Vite and started the dev server. Along the way we naturally saw code like this:
function App() {
return (
<div>
<h1>Hello, React!</h1>
</div>
);
}Inside a function, code that looks like HTML is being returned directly. HTML inside JavaScript — doesn’t that feel a bit odd? In this post we’ll look at this curious syntax called JSX and how to use it.
What is JSX? #
JSX (JavaScript XML) combines JavaScript with markup syntax that looks like HTML. It isn’t part of official JavaScript, but it’s an extension proposed alongside React when Facebook released it, and today nearly every React project uses JSX.
You can actually write React without JSX. The two snippets below produce exactly the same result.
function App() {
return <h1>Hello</h1>;
}function App() {
return React.createElement('h1', null, 'Hello');
}Everyone agrees the upper one is much easier to read. That’s why we use JSX.
The browser doesn’t understand JSX #
The browser’s JavaScript engine cannot understand JSX directly. The JSX we write is converted into plain JavaScript during the build step before being shipped to the browser. Build tools like Vite handle this conversion automatically, so we can use JSX freely without worrying about it.
JSX basic rules #
JSX looks similar to HTML but has a few different rules. These are where beginners get tripped up most often, so we’ll go through them carefully.
Rule 1. Wrap with a single parent element #
JSX is ultimately a function returning something, so it can only return one value at a time. To return multiple elements, you must wrap them in a single parent.
function App() {
return (
<h1>Hello</h1>
<p>Have a great day</p>
);
}function App() {
return (
<div>
<h1>Hello</h1>
<p>Have a great day</p>
</div>
);
}Wrapping with a <div> works, but you might not love piling on meaningless <div>s. In that case you can use the empty tag — a Fragment.
function App() {
return (
<>
<h1>Hello</h1>
<p>Have a great day</p>
</>
);
}<>...</> is shorthand for React.Fragment. It groups multiple children together without rendering any actual element to the DOM.
Rule 2. Every tag must be closed #
In HTML, tags like <img>, <br>, and <input> could be left unclosed. In JSX, however, every tag must be closed. Tags with no children get a trailing / (self-closing).
<img src="/cat.png" alt="cat" />
<br />
<input type="text" />Rule 3. className instead of class
#
The class attribute used for CSS classes in HTML clashes with a JavaScript reserved word, so JSX uses className instead.
<div className="card">This is a card</div>For the same reason, the for attribute on <label> becomes htmlFor.
<label htmlFor="email">Email</label>
<input id="email" type="email" />Rule 4. Attribute names use camelCase #
In HTML, attribute names are all lowercase (onclick, tabindex, readonly), but in JSX you use camelCase.
<button onClick={handleClick} tabIndex={0}>Click</button>
<input readOnly value="fixed value" />Embedding JavaScript expressions in JSX #
JSX’s real power is the ability to freely embed JavaScript expressions inside the markup. Anything inside curly braces { } is evaluated as a JavaScript expression, and its result is rendered.
function App() {
const name = 'Cheolsu';
const age = 30;
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
<p>In 10 years: {age + 10}</p>
</div>
);
}Function calls, ternary operators, array methods — anything that’s an expression works.
function App() {
const isLoggedIn = true;
const items = ['apple', 'banana', 'cherry'];
return (
<div>
<h1>{isLoggedIn ? 'Welcome!' : 'Please log in'}</h1>
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
</div>
);
}if or for loops cannot go in directly. For conditional output, use the ternary or && operator; for repeated output, use the map method. We’ll cover this in detail in #7 Conditional Rendering and #8 Lists and key.Inline styles #
To set styles directly in JSX, pass them as an object. CSS property names also use camelCase.
function App() {
return (
<h1 style={{ color: 'tomato', fontSize: '32px' }}>
A styled heading
</h1>
);
}The double curly braces look odd at first, but the outer { } means “a JavaScript expression in JSX,” and the inner { } is a “JavaScript object literal.”
Comments inside JSX #
To add comments inside JSX, wrap them in curly braces, since they must be JavaScript expressions.
function App() {
return (
<div>
{/* This is a JSX comment */}
<h1>Title</h1>
</div>
);
}Try it yourself #
Change the src/App.jsx from your last project to this:
function App() {
const name = 'Cheolsu';
const fruits = ['apple', 'banana', 'cherry'];
return (
<>
<h1>Hello, {name}!</h1>
<p>Today's fruits ({fruits.length} total):</p>
<ul>
{fruits.map(fruit => <li key={fruit}>{fruit}</li>)}
</ul>
</>
);
}
export default App;Save and HMR will refresh the screen instantly. Try changing the variables or adding a new fruit to the array, and watch how the screen reacts.
Wrapping up #
In this post we introduced JSX, React’s core syntax, and looked at the four basic rules (wrapping with a parent, closing tags, className/htmlFor, camelCase attributes), embedding JS expressions, inline styles, and comments. JSX should feel a lot less strange now.
In the next post, “React Basics #4: Components and Props,” we’ll cover components — the central concept for breaking the screen into small units — along with props, the channel for passing data into a component.