Skip to the chapter
←ReactGGroundwork⚛React
React/Beginner/B1
1 / 57 written chapters
/

The cover
  1. B1Setup & JSX
  2. B2Thinking in React
  3. B3Components
  4. B4Props
  5. B5useState
  6. B6Events & conditionals
  7. B7Lists & keys
  8. B8Forms
  9. B9useEffect
  10. B10Lifting state & styling
  11. B11Guided project
  12. ★Cheat page
  1. I1useRef
  2. I2Context
  3. I3useReducer
  4. I4Custom hooks
  5. I5Effects in depth
  6. I6Routing
  7. I7State architecture
  8. I8Memoisation
  9. I9Boundaries & portals
  10. I10Data fetching
  11. I11Forms at scale
  12. I12State libraries
  13. I13Auth
  14. I14TanStack Query
  15. I15Testing
  16. I16TypeScript
  17. I17Animation
  18. I18E2E testing
  19. I19i18n
  20. I20File uploads
  21. I21Real-time UI
  22. I22shadcn/ui
  23. ★Cheat page
  1. A1Fiber
  2. A2Concurrent
  3. A3Suspense
  4. A4Server Components
  5. A5Server Actions
  6. A6use()
  7. A7Rendering strategies
  8. A8Effect timing
  9. A9External stores
  10. A10Refs in depth
  11. A11Component patterns
  12. A12Performance
  13. A13Accessibility
  14. A14Design systems
  15. A15Advanced TypeScript
  16. A16Architecture at scale
  17. A17Custom renderers
  18. A18Migrating & upgrading
  19. A19Production debugging
  20. A20Review & judgement
  21. A21State machines
  22. A22Feature flags
  23. A23Interview bank
  24. ★Cheat page

⚛B1 · Part 1 · Beginner

Project setup & JSX

JSX is not HTML and it is not a template language — it is a function call wearing a costume.

5 min read9 sections2 exercises
1 of 57

In this chapter

  • Starting a project
  • Where the app actually starts
  • What JSX actually is
  • The rules that trip everyone up
  • What React renders and what it skips

and 4 more

about 5 min
09On this page
  1. Starting a project
  2. Where the app actually starts
  3. What JSX actually is
  4. The rules that trip everyone up
  5. What React renders and what it skips
  6. Two transforms, and why old code imports React
  7. Comments and whitespace
  8. The compile errors you will actually see
  9. Escaping is automatic

Starting a project

Two commands cover almost everything you will do. Vite for a plain React app that runs entirely in the browser, and Next.js when you want routing and a server as well.

npm create vite@latest my-app -- --template react-ts
npm create next-app@latest my-app

The old create-react-app is no longer maintained. If you find a tutorial that starts with it, the rest of the tutorial is old too.

Where the app actually starts

// main.tsx — the file every tutorial skips
import { createRoot } from "react-dom/client";
import App from "./App";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

createRoot takes a real DOM node and hands React control of everything inside it. That is the only place React and the document meet — from here down, React owns the tree.

For a server-rendered page the call is hydrateRoot instead: the HTML already exists, so React attaches to it rather than building it. Using createRoot on server HTML throws it away and re-renders from scratch, which quietly discards the whole point of rendering on the server.

<StrictMode> is development-only and adds nothing to the production build. It deliberately double-invokes components and effects to surface impure renders and missing cleanups — which is why your effect runs twice locally and once in production.

What JSX actually is

This is the single most useful thing to understand on day one, because everything confusing about React later makes sense once you have it.

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

Before your code runs, the build step turns that into a function call:

const el = jsx("h1", { className: "title", children: "Hello" });

Which returns a plain object — roughly { type: "h1", props: { className: "title", children: "Hello" } }. That object is called a React element, and it is just a description of what you want on screen. Nothing has touched the DOM at this point.

Three things this explains
  • JSX is an expression. You can put it in a variable, return it from an if, or hold it in an array — because it evaluates to an object.
  • You cannot have two sibling tags at the top level, because a function returns one value.
  • React elements are cheap. Creating them is making small objects, not touching the page.

The rules that trip everyone up

One root element

Wrong, and it will not compile:

return (
  <h1>Title</h1>
  <p>Body</p>
);

Wrap them. If you do not want a real <div> in the output, use a fragment — the empty tag:

return (
  <>
    <h1>Title</h1>
    <p>Body</p>
  </>
);

Braces mean "back to JavaScript"

Inside JSX, { } is an escape hatch to a JavaScript expression — something that produces a value.

const name = "Ana";
const age = 30;

<p>{name} is {age} next year: {age + 1}</p>
<p>{name.toUpperCase()}</p>          // calls are fine
<p>{age > 18 ? "adult" : "minor"}</p>  // ternary is fine

A statement is not. {if (x) ...} and {for (...) ...} are syntax errors, because if and for do not produce a value. That constraint is why React code leans on &&, ternaries and .map() — not style, arithmetic.

Attributes are props, so they are camelCase

HTMLJSXWhy
classclassNameclass is a reserved word in JavaScript
forhtmlForsame reason
onclickonClickthey are object keys, and JavaScript is camelCase
tabindextabIndexsame
style="color: red"style={{ color: "red" }}an object, not a string

The double braces in style are not special syntax. The outer pair is "back to JavaScript", the inner pair is an object literal.

Self-closing tags are required

<img> and <br> are legal HTML. In JSX they must be <img /> and <br />, because the compiler needs to know where the element ends.

What React renders and what it skips

<p>{"text"}</p>      // text
<p>{42}</p>          // 42
<p>{null}</p>        // nothing
<p>{undefined}</p>   // nothing
<p>{false}</p>       // nothing
<p>{[1, 2, 3]}</p>   // 123
<p>{0}</p>           // 0  ← not nothing

null, undefined, true and false render nothing, which is what makes {condition && <Thing />} work. Zero is the exception, and it is the most common rendering bug in React — {items.length && <List />} puts a literal 0 on the page when the list is empty. Conditional rendering covers the fix.

Two transforms, and why old code imports React

Before React 17, JSX compiled to React.createElement(...), so React had to be in scope — hence import React from "react" at the top of every file, even ones that never mentioned it. That was the classic runtime.

The automatic runtime compiles to jsx() imported from react/jsx-runtime by the compiler itself. You no longer import React to write JSX. You still import it for hooks, but import { useState } from "react" is the modern form.

// classic — what you will see in older tutorials
import React from "react";
React.createElement("h1", { className: "title" }, "Hello");

// automatic — what your build actually emits today
import { jsx as _jsx } from "react/jsx-runtime";
_jsx("h1", { className: "title", children: "Hello" });

If a tutorial tells you a missing React import is why your JSX broke, it is written for a version you are not using.

Comments and whitespace

<div>
  {/* a JSX comment — braces, then a block comment */}
  <span>a</span> <span>b</span>      // space between them: kept
  <span>a</span>
  <span>b</span>                      // newline between them: removed
  <span>a</span>{" "}
  <span>b</span>                      // {" "} puts it back
</div>

JSX strips whitespace that includes a newline at the start or end of a line. That is why two elements on separate lines run together, and why {" "} exists — it is an explicit space that survives formatting. A missing space after a link is nearly always this.

The compile errors you will actually see

MessageWhat it means
Adjacent JSX elements must be wrappedTwo siblings at the top level. Add a fragment.
Objects are not valid as a React childYou rendered an object — often a whole record instead of a field, or a Date. Render a string.
Unexpected token, expected ","Usually a stray class= or an unclosed tag a few lines above.
Each child in a list should have a unique "key"A .map() without keys — see lists and keys.
Functions are not valid as a React child{handleClick} where you meant {handleClick()}, or a component used without angle brackets.

Escaping is automatic

Anything you interpolate is escaped before it reaches the DOM, so {"<img onerror=alert(1)>"} renders as visible text rather than as a tag. That is React's main defence against XSS, and it is on by default — the only way past it is dangerouslySetInnerHTML, which is named that way deliberately.

Interview answer, one sentence

"JSX is syntax sugar for function calls that return plain objects describing the UI. React reads those objects and works out what to change in the DOM, which is why JSX is an expression and why the rules feel like JavaScript rules rather than HTML rules."

02Practice this chapter

Opens in the editor: write it, run it, and check it against real tests.

  • 7 testsAn expression, not a template stringbeginner
  • 7 testsReturn two things without an extra divbeginner
All 46 for this level →
B1

Finished reading?

0 of 57 chapters read in this series

→Next stop

  1. Back toThe cover
  2. B1you are here
  3. Next · B2Thinking in ReactTurning a picture into a component tree — the step that happens before you type anything.3 min read

01On this page

9 sections · 0%

  1. 01Starting a project
  2. 02Where the app actually starts
  3. 03What JSX actually is
  4. 04The rules that trip everyone up
  5. 05What React renders and what it skips
  6. 06Two transforms, and why old code imports React
  7. 07Comments and whitespace
  8. 08The compile errors you will actually see
  9. 09Escaping is automatic

[] chapters · t top