React Basics #3: What Is JSX?

5 min read

Last time we created our first React project with Vite and started the dev server. Along the way we naturally saw code like this:

src/App.jsx
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.

With JSX
function App() {
  return <h1>Hello</h1>;
}
Without JSX
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.

Incorrect
function App() {
  return (
    <h1>Hello</h1>
    <p>Have a great day</p>
  );
}
Correct
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.

Using 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).

JSX
<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.

JSX
<div className="card">This is a card</div>

For the same reason, the for attribute on <label> becomes htmlFor.

JSX
<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.

JSX
<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.

src/App.jsx
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.

src/App.jsx
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>
  );
}
Note
Only expressions are allowed inside the curly braces. Statements like 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.

src/App.jsx
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.

src/App.jsx
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:

src/App.jsx
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.

Tip
When you get a build error in JSX code, nine times out of ten you forgot one of the four rules above. Run through this checklist: “Did I wrap multiple elements with a parent?”, “Did I close every tag?”, “Did I use className instead of class?”, “Did I use camelCase for attributes?” and most issues resolve themselves.

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.

X