Chapter 03 of 14 · browse all
Chapter 03

JSX

The syntax that lets you write markup inside JavaScript, and the handful of rules that differ from HTML.

JSX is a syntax, not a template language

JSX looks like HTML sitting inside JavaScript. It is neither string templating nor HTML — it is syntax sugar that a build tool converts into function calls.

const heading = <h1 className="title">Hello</h1>;

becomes, roughly:

const heading = React.createElement(
  "h1",
  { className: "title" },
  "Hello"
);

Because JSX is just JavaScript, an element is an ordinary value. You can store it in a variable, put it in an array, return it from a function, or pass it as an argument.

Embedding expressions

Curly braces switch from markup back to JavaScript. Anything that evaluates to a value can go inside them.

const user = { first: "Ada", last: "Lovelace" };

function Profile() {
  return (
    <div>
      <h1>{user.first + " " + user.last}</h1>
      <p>Name length: {user.first.length}</p>
      <p>Today: {new Date().toDateString()}</p>
    </div>
  );
}

Braces take expressions, not statements. An if block or a for loop will not work inside them; a ternary or a call to map will.

One root element

An expression produces one value, so JSX must return a single element. Wrapping in a div works but adds a node you may not want.

// Not valid — two siblings at the top level
return (
  <h1>Title</h1>
  <p>Body</p>
);

// Valid — a fragment, which renders nothing itself
return (
  <>
    <h1>Title</h1>
    <p>Body</p>
  </>
);

The empty tags are a fragment. Use the longer form React.Fragment when you need to give it a key inside a list.

Attributes that differ from HTML

JSX attribute names follow JavaScript's DOM property names, which are camelCase, and avoid reserved words.

  • class becomes className
  • for becomes htmlFor
  • onclick becomes onClick, onchange becomes onChange
  • tabindex becomes tabIndex
  • data- and aria- attributes keep their dashes
<label htmlFor="email" className="field-label">Email</label>
<input id="email" type="email" aria-required="true" />

Closing every tag

HTML tolerates unclosed tags. JSX does not. Void elements such as img, input, and br must be self-closed.

<img src="/logo.svg" alt="Logo" />
<input type="text" />
<br />

Inline styles are objects

The style attribute takes an object, not a string. Property names are camelCase and numeric values default to pixels.

const box = {
  backgroundColor: "#e8f4f8",
  paddingTop: 12,
  borderRadius: 8,
};

return <div style={box}>Styled</div>;

The double braces you often see — style={{ color: "red" }} — are not special syntax. The outer pair enters JavaScript, the inner pair is an object literal.

Comments

return (
  <div>
    {/* This is a comment inside JSX */}
    <p>Visible</p>
  </div>
);

JSX escapes values for you

Anything you interpolate is inserted as text, so a string containing markup shows up as characters rather than being parsed as HTML. This makes cross-site scripting hard to introduce by accident.

const input = "<script>alert(1)</script>";

// Renders the characters, does not run anything
return <p>{input}</p>;