Command Palette

Search for a command to run...

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

Topic 1.1

What JSX Is

In one line

JSX is a syntax extension for JavaScript that lets you write HTML-like tags inside your code; a compiler turns every tag into an ordinary function call that returns a plain object describing the UI.

Think of it like this

Writing a shopping list in shorthand. You write 2 milk, 1 bread because it is quick to read, but the shopkeeper understands it as "two bottles of milk and one loaf of bread". JSX is shorthand for UI. You write <h1>Hello</h1> because it looks like the page you want. Before the browser runs it, a tool translates the shorthand into the long form: a JavaScript function call.

Words you'll meet

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

JSX
HTML-like tags written inside JavaScript, such as <h1>Hi</h1>. Short for JavaScript XML.
Syntax extension
Extra grammar added to a language. JSX adds tags to JavaScript.
Compiler
A program that turns code into other code. Here it turns JSX into plain JavaScript function calls.
Expression
A piece of code that produces a value, like 2 + 3, name or <p>Hi</p>.
jsx() call
The function call a JSX tag turns into. It returns a plain object describing the element.
createElement
React's older function for making elements by hand: createElement("p", null, "Hi"). Still works.
React element
The plain object a JSX tag produces. It describes what should be on screen; it is not a real DOM element.
Separation of concerns
Keeping each job in its own place. React groups by piece of UI, not by file type.

Step by step

01You write tags inside JavaScript

Here is a small component. The part after return looks like HTML, but it lives inside a JavaScript function and uses a JavaScript variable, name.

Greeting.jsxwhole filejsx
function Greeting() {
  const name = "Asha";
  return <h1 className="title">Hello, {name}!</h1>;
}

02The compiler turns each tag into a function call

Before the browser sees this file, the compiler rewrites the tag. The tag name becomes the first argument; the attributes and the children become one object, the props. Nothing else in the file changes.

Greeting.js (compiled)whole filejavascript
import { jsxs as _jsxs } from "react/jsx-runtime";

function Greeting() {
  const name = "Asha";
  return _jsxs("h1", { className: "title", children: ["Hello, ", name, "!"] });
}

03The call returns a plain object

Calling jsx() doesn't touch the page. It returns a small object, a React element, that says "an h1 with class title and this text". It is a description, like a drawing of a house is not a house.

04React DOM builds the real page from the description

When you render, react-dom reads the element objects and creates (or updates) real DOM elements to match. JSX → function calls → element objects → DOM.

React DOM builds the real page from the descriptiondiagram
Rendering diagram…

05Because it is a value, you can move it around

Since a tag is really a function call, you can put it anywhere a value fits: in a variable, in an if, in an array, as an argument. The live examples below do exactly that.

Try it yourself

  1. 1

    Change the hour

    In "JSX is a value", predict which heading appears when hour is 12. Change it and check. (12 < 12 is false, so it's the evening greeting.)

  2. 2

    Add a third card with function calls

    In the first example, write a third card with createElement that says Bye! in an h2, and render it inside <main> after the others. Notice how much harder the nested calls are to read than the JSX.

  3. 3

    Pass JSX as an argument

    Write function framed(content) { return <div className="card">{content}</div>; } and render {framed(<b>Inside a frame</b>)}. JSX went into a function and came back wrapped.

Code & diagrams

The same card written with JSX and with function calls New tab

Two identical cards: the JSX version and the hand-written `createElement` version produce the same kind of object. JSX is only a nicer way to write the calls.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

JSX gives a div with class card
Calls give a div with class card
JSX is a value: store it, return it, choose it New tab

`greeting` is an object, so it can sit in a variable and be dropped into the page later with `{greeting}`. Change `hour` to 18 to see the other greeting.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

greeting is an object
What the compiler writes for the card (automatic runtime)javascript
// Input (JSX)
<div className="card">
  <h2>Hello, Asha!</h2>
  <p>You have 3 new messages.</p>
</div>

// Output (real sucrase output, tidied): one call per tag
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";

_jsxs("div", {
  className: "card",
  children: [
    _jsx("h2", { children: "Hello, Asha!" }),
    _jsx("p", { children: "You have 3 new messages." }),
  ],
});
From JSX to the screendiagram
Rendering diagram…

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

Put quotes around JSX

In the second example, change the morning greeting to "<h2>Good morning, {name}!</h2>" (with double quotes around it).

terminal
$ Preview
── what you'll see ──
<h2>Good morning, {name}!</h2>
(shown as plain text, tags and braces included)

Myth vs fact

Myth

JSX is HTML inside JavaScript.

Fact

It only looks like HTML. It is JavaScript syntax that compiles to function calls, with its own rules: className, camelCase attributes, every tag closed (Topic 1.2).

Myth

Browsers understand JSX.

Fact

No browser runs JSX. A compiler turns it into plain JavaScript first, either in your build tool (Vite, Next.js) or, on Hectal, in the live editor.

Myth

You must use JSX to use React.

Fact

JSX is optional. createElement (or jsx() from react/jsx-runtime) builds the same elements. JSX is simply what nearly everyone uses.

Myth

Mixing markup and logic breaks separation of concerns.

Fact

It separates by component instead of by file type. The markup and logic of one button change together, so keeping them together is the cleaner split.

Pro corner

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

  • ▸

    JSX has a written specification (facebook/jsx on GitHub) that defines only the syntax, not what it means. The meaning comes from the compiler setting: jsx: "react-jsx" in tsconfig.json, or the JSX options of Babel, SWC, esbuild or Oxc.

  • ▸

    The automatic runtime (introduced with React 17) imports jsx/jsxs from react/jsx-runtime for you. Before it, the classic runtime compiled tags to React.createElement(...), so every file needed import React from "react". That is why old code starts with that import even when React looks unused.

  • ▸

    In development builds the compiler emits jsxDEV(...) from react/jsx-dev-runtime instead, passing extra information (such as whether the children were static) so React can print better warnings. The live editor here compiles in production mode (jsx/jsxs) but still runs React's development build, so you still see its warnings.

Remember this

  1. 1

    JSX stands for JavaScript XML. It is a syntax extension: extra grammar added on top of JavaScript, the way a dialect adds a few words to a language. It lets you write tags such as <button>Save</button> in the middle of JavaScript code. JSX is not HTML (it has its own rules, Topic 1.2), not a string (there are no quotes around it), and not a template language (it has no special loops or ifs; you use plain JavaScript for those, Topic 1.3).

  2. 2

    Browsers do not understand JSX. Before your code runs, a compiler (a program that turns code into other code) rewrites every tag into a function call. With React's automatic JSX runtime, <h1 className="title">Hello</h1> becomes jsx("h1", { className: "title", children: "Hello" }), imported from react/jsx-runtime. Tools that do this include Babel, SWC, esbuild, Oxc, TypeScript and sucrase (which compiles the examples on this page). You never call jsx() yourself; you'll look inside it in Topic 1.7.

  3. 3

    Because each tag becomes a function call, JSX is an expression: something that produces a value. You can store JSX in a variable, return it from a function, pass it as an argument, put it in an array or choose between two pieces with a ternary. Anything you can do with a value in JavaScript, you can do with JSX.

  4. 4

    Why mix markup and logic in one file? Older web code split files by technology: HTML in one file, JavaScript in another. But the markup of a button and the code that decides what it shows always change together. React splits code by concern instead: one component holds the markup, the logic and (often) the styles for one piece of the screen. JSX is what makes that comfortable to write.

  5. 5

    JSX is optional. You can build any React app by calling createElement("h1", null, "Hello") yourself, and a few projects do. In practice almost every React codebase, tutorial and job uses JSX, because nested tags are far easier to read than nested function calls. Files that contain JSX usually end in .jsx (or .tsx with TypeScript, Phase 12).

  6. 6

    JSX is not only for React. The syntax just produces function calls, so other libraries (Preact, Solid, Vue's JSX mode) use it with their own functions. Which function the tags turn into is a compiler setting, which is why your build tool needs to know you're using React.

Explain it without notes

01

What is JSX, and what happens to it before the browser runs your code?

02

Why can you store JSX in a variable or return it from a function?

03

Why does React put markup and logic in the same file?

04

Is JSX required to use React? What is the alternative?

Practice

01

Rewrite <p className="note">Saved!</p> using createElement.

02

Write a function label(text) that returns a <strong> element containing the text, and use it twice inside a <p>.

Trade-offs

  • ↔

    JSX needs a compile step. That's free in any modern toolchain, but a page that loads React from a <script> tag with no build has to use createElement (or a tagged-template library such as htm) instead.

  • ↔

    Keeping markup and logic together is clear for small components; large components that mix too much logic into their JSX become hard to read, which is why you'll learn to split components (Phase 2) and extract hooks (Phase 10).

Done when you can

  • I can say what JSX is and why browsers can't run it directly.

  • I can describe what a JSX tag compiles to.

  • I can store JSX in a variable and return it from a function.

  • I can explain why React keeps markup and logic together.

  • I know JSX is optional and what createElement does.