JSX

Write markup inside JavaScript, and know the handful of places it differs from HTML.

03of 20
≈ 11Min read
8Parts

JSX is a function call

That is the whole of it. JSX is syntax for building an object that describes what to render — not a template, not a string, and not HTML.

what the compiler turns it into
<h1 className="title">Doors</h1>

// becomes
React.createElement("h1", { className: "title" }, "Doors")

Expressions in braces

any expression, not any statement
<p>{door.name}</p>
<p>{door.days * 100}</p>
<p>{door.days > 7 ? "slow" : "quick"}</p>
<p>{items.map((i) => <li key={i.id}>{i.text}</li>)}</p>
<img src={door.image} alt={door.name} />
<div className={`card ${door.archived ? "is-archived" : ""}`} />

// NOT allowed: if, for, switch — they are statements
// do the work above the return instead
Braces are not quotes

className="card" is the string "card". className={card} is the value of a variable called card. style="color: red" does not work at all — see below.

The differences from HTML

HTMLJSXWhy
classclassNameclass is a reserved word
forhtmlForsame reason
onclickonClickcamelCase for every event
tabindextabIndexcamelCase for most attributes
style="color:red"style={{ color: "red" }}an object, with camelCase keys
<br><br />every tag must close
&nbsp;{"\u00a0"}entities are not parsed
data-id, aria-labelunchangedthese two keep their dashes

One parent, or none

Use a fragment when the wrapper would break the layout — a table row, a flex or grid child, a list item.

a component returns one thing
// error: two roots
return (
  <h1>Doors</h1>
  <p>Two of them</p>
);

// a wrapper
return (
  <div>
    <h1>Doors</h1>
    <p>Two of them</p>
  </div>
);

// or a fragment — no element in the DOM at all
return (
  <>
    <h1>Doors</h1>
    <p>Two of them</p>
  </>
);

// the long form, when you need a key
<React.Fragment key={id}>…</React.Fragment>

Whitespace and text

the rules that surprise people
// newlines and indentation between elements are removed
<p>
  {door.name}
  {door.days}
</p>                      // "Painted5" — no space

<p>
  {door.name} {door.days}
</p>                      // "Painted 5" — a space on the same line

<p>{door.name}{" "}{door.days}</p>    // explicit, and survives a formatter

// a comment
{/* this is a JSX comment */}

Conditional rendering

React renders 0 because it is a valid child. false, null and undefined render nothing. Always make the left side a boolean.

three forms and one trap
{isOpen && <Panel />}                       // and
{isOpen ? <Panel /> : <Placeholder />}      // ternary
{isOpen ? <Panel /> : null}

// THE TRAP: && with a number
{items.length && <List items={items} />}
// when items is empty this renders "0" on the page

{items.length > 0 && <List items={items} />}   // a real boolean

Escaping is automatic

Every value in braces is escaped. That single behaviour removes the commonest security bug on the web — which is also why the escape hatch is named so awkwardly.

the one genuinely safe default
<p>{userText}</p>
// "<img src=x onerror=…>" appears as TEXT, whatever it contains

// the only way to inject markup, and the name is a warning
<p dangerouslySetInnerHTML={{ __html: trustedHtml }} />

Children and composition

anything between the tags
function Panel({ title, children }) {
  return (
    <section className="panel">
      <h2>{title}</h2>
      {children}
    </section>
  );
}

<Panel title="Specification">
  <p>Aluminium frame.</p>
  <p>1 mm gap.</p>
</Panel>

// several slots: pass elements as props
<Layout sidebar={<Nav />} main={<Article />} />

Practice