Command Palette

Search for a command to run...

Hectal
PHASE 1Beginner ~20 min· topic 2 of 7

Topic 1.2

The Rules of JSX

In one line

JSX looks like HTML but is stricter: a component returns one root element (or a Fragment), every tag must be closed, and attributes use JavaScript names such as className, htmlFor and camelCase like onClick and tabIndex.

Think of it like this

Packing for a trip with one bag allowed. You can take many things, but they must all go inside one bag. If you need a bag but don't want an extra one showing, you use a packing cube that disappears inside the suitcase. A component may return many elements, but they must sit inside one root, and a Fragment is the invisible packing cube.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Root element
The single outermost element a component returns. Everything else sits inside it.
Fragment
An invisible wrapper, <> … </>, that groups elements without adding anything to the page.
Self-closing tag
A tag that closes itself with />, like <br /> or <Avatar />.
camelCase
Joining words with each new word capitalised: tabIndex, onClick, maxLength.
className
JSX's name for the HTML class attribute, because class is a reserved word in JavaScript.
htmlFor
JSX's name for the for attribute of <label>, because for is a JavaScript keyword.
Reserved word
A word JavaScript keeps for its own grammar, such as class, for, if or return.
Syntax error
Code the compiler can't understand, so the program can't run at all.

Step by step

01Start from HTML that breaks the rules

This is valid HTML, but as JSX it breaks three rules: two roots, unclosed <input> and <br>, and the class and for attributes.

snippet.htmlwhole filehtml
<label class="label" for="email">Email</label>
<input id="email" type="email" maxlength="40"><br>
<p class="hint">We never share it.</p>

02Wrap everything in one root

Add a Fragment around the three pieces. Nothing extra appears in the page, but the component now returns one value. Use a real element (like <div className="field">) instead when you want a box to style.

03Close every tag

<input …> becomes <input … /> and <br> becomes <br />. The space before /> is optional but customary.

04Rename the attributes

class → className, for → htmlFor, maxlength → maxLength. Values stay the same. The result:

EmailField.jsxwhole filejsx
function EmailField() {
  return (
    <>
      <label className="label" htmlFor="email">Email</label>
      <input id="email" type="email" maxLength={40} /><br />
      <p className="hint">We never share it.</p>
    </>
  );
}

05Why the outer wrapper matters to the compiler

Each tag is a call. Two calls in a row with nothing between them isn't valid JavaScript, while a Fragment is one call whose children array holds the others.

Why the outer wrapper matters to the compilerdiagram
Rendering diagram…

Try it yourself

  1. 1

    Swap the Fragment for a div

    In the glossary, change <Fragment key={t.word}> … </Fragment> to <div key={t.word}> … </div>. Predict the first log line, then run. (DIV DIV DIV: now the <dl> holds divs, which HTML only allows in limited cases and which screen readers may not announce as term/definition pairs.)

  2. 2

    Self-close your own component

    In the glossary, <Glossary /> is self-closed. Write it as <Glossary></Glossary> instead. Nothing changes: a component with no children can be written either way.

  3. 3

    Use the HTML names on purpose

    In the form, change className to class and htmlFor to for on the first label. Predict: does the page still work? Run it and read the two new warning lines at the top of the console. React still writes the attributes, but it tells you the JSX names.

Code & diagrams

Fragments: group without adding a wrapper New tab

Neither Fragment adds an element: the `<dl>` holds only `dt`/`dd` pairs, and the root holds the `h2` and the `dl` directly. The long form `<Fragment key=…>` is needed inside `map` because the short `<>` can't take a key.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Children of <dl>: DT DD DT DD DT DD
Children of #root: H2 DL
A form that follows every rule New tab

You write `htmlFor`, `maxLength`, `autoComplete`, `readOnly` and `tabIndex`; React writes the normal HTML attributes (`for`, `maxlength`, …) into the page. Click the word `Email`: the input gets focus, because the label is linked to it. `readOnly` alone means `true`, like a boolean attribute in HTML.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

<label for="email">Email</label>
<input id="email" maxlength="40" autocomplete="email" type="email">
<input id="code" readonly="" tabindex="-1" value="HECTAL">

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Return two elements without a wrapper

In the form example, delete the line <form className="form"> and the line </form>, so the component returns several elements side by side.

terminal
$ Console
── what you'll see ──
Uncaught SyntaxError: Unexpected token, expected "," (6:14)

Break #2

Leave a tag open

In the form example, change <br /> to <br>.

terminal
$ Console
── what you'll see ──
Uncaught SyntaxError: Unexpected token (7:14)

Break #3

Write `class` and `for`

In the form example, change className="form" to class="form" and the first htmlFor="email" to for="email".

terminal
$ Console
── what you'll see ──
Invalid DOM property `for`. Did you mean `htmlFor`?
Invalid DOM property `class`. Did you mean `className`?

Myth vs fact

Myth

Wrapping everything in a <div> is always fine.

Fact

Extra divs can break CSS layouts (a grid or flex parent expects specific children) and HTML structure (<ul> must contain <li>, <tr> must contain cells). Use a Fragment when you only need grouping.

Myth

<> and <Fragment> are different things.

Fact

They're the same Fragment. <> is the short form; only the long form can take a key.

Myth

Self-closing tags are only for void HTML elements like <br>.

Fact

In JSX any element or component without children can self-close: <div />, <textarea />, <Avatar />.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Attribute names in JSX follow React's own prop names, which are mostly the DOM property names (tabIndex, readOnly, htmlFor), not the HTML attribute names. React maps them back when it writes attributes. That's why value and defaultValue differ: defaultValue sets the initial value attribute, value makes the field controlled (Phase 5).

  • ▸

    Unknown lowercase attributes are passed through to the DOM since React 16 (with a warning only for likely mistakes like class), so custom attributes and web-component attributes work. Booleans for non-boolean attributes still warn.

  • ▸

    A component may also return a string, a number, an array, or null (Topic 1.4). The "one root" rule is really "one value": an array of elements is one value too, though Fragments are clearer and arrays need keys.

Remember this

  1. 1

    Rule 1: one root element. A component returns one value, and a JSX tag is one function call. Writing return <h1>Hi</h1> <p>There</p>; would mean returning two calls side by side, which JavaScript can't do. So wrap them: in a real element such as <div> or <section>, or in a Fragment, written <> … </>. A Fragment groups children without adding any element to the DOM, so it doesn't break CSS layouts (flex, grid) or HTML structures (<ul>, <table>, <dl>) that expect particular children.

  2. 2

    The short <></> syntax can't take attributes. When you need a key (in a list, Topic 3.3) use the long form: import { Fragment } from "react" and write <Fragment key={id}> … </Fragment>. key is the only prop a Fragment accepts in normal React code.

  3. 3

    Rule 2: close every tag. In HTML, <br>, <img> and <input> have no closing tag. In JSX every element must be closed, either with a closing tag (<p></p>) or by self-closing it with /> (<br />, <img src="a.png" alt="" />). Any element without children may self-close, including <div /> and your own components (<Avatar />). An unclosed tag is a syntax error: the compiler stops and nothing renders.

  4. 4

    Rule 3: attributes use JavaScript names. JSX attributes become keys of a JavaScript object (the props), and they follow the names of DOM properties, which are camelCase (each word after the first starts with a capital): onClick, tabIndex, maxLength, readOnly, autoComplete, autoFocus. Two are renamed because class and for are reserved words in JavaScript: write className and htmlFor. React writes the normal HTML attribute into the page (class="…", for="…").

  5. 5

    If you use the HTML spelling by mistake, React's development build warns, for example `Invalid DOM property class. Did you mean className?. Two families keep their dashes: data-* and aria-* attributes (data-id, aria-label`), which you'll see in Topic 1.6.

  6. 6

    Rule 4: component names start with a capital letter (from Topic 0.2). <Card /> calls your Card function; <card /> would be treated as an HTML tag named card. Attribute values are either a string in quotes (type="email") or a JavaScript expression in curly braces (maxLength={40}), which is the next topic.

Explain it without notes

01

Why must a component return a single root element?

02

What is a Fragment, and when would you use <Fragment> instead of <>?

03

Why do we write className and htmlFor in JSX?

04

Which tags must be self-closed in JSX, and what happens if you forget?

Practice

01

Convert to JSX: <img src="cat.png" alt="A cat"><label for="pet" class="big">Pet</label>, returned from a component.

02

Render a list of three products as <dt>/<dd> pairs inside a <dl> with no extra wrapper elements.

Trade-offs

  • ↔

    A Fragment keeps the DOM clean, but you can't style or attach an event handler to it. When you need a box to style or click, use a real element.

  • ↔

    Strict closing rules feel fussy coming from HTML, but they make JSX unambiguous: the compiler never has to guess where an element ends.

Done when you can

  • I can wrap several elements in one root or a Fragment.

  • I know when to use <Fragment key> instead of <>.

  • I close every tag and self-close empty ones.

  • I write className, htmlFor and camelCase attributes.

  • I can read React's "Invalid DOM property" warning and fix it.