Free lesson · React Deep Dive
What JSX Is (and What It Isn't)
What JSX Is (and What It Isn't)
The thesis of this lesson is one sentence, and I'll repeat it through the whole module: JSX is JavaScript. That's it. Not HTML, not a templating language, not a fancy syntax bolted onto React. JSX is plain JavaScript with a syntax extension that lets you write XML-like markup as expressions. It compiles to ordinary function calls. There's no runtime magic.
The reason this sentence matters is that almost every JSX confusion you'll ever have can be resolved by remembering it. "Why can't I put an if statement here?" Because JSX is JavaScript and you're inside a value expression. "Why is it className instead of class?" Because JSX is JavaScript and class is a reserved word. "Why does this && render 0 instead of nothing?" Because JSX is JavaScript and 0 && X evaluates to 0. The pattern repeats. Every JSX rule has a JavaScript reason.
This lesson sets up the framing. The next four lessons earn it.
What You're Looking At
A piece of JSX:
const greeting = <h1 className="title">Hello, {name}</h1>
To a fresh eye, this looks like HTML embedded in JavaScript. It isn't. The HTML-like bit - <h1 className="title">Hello, {name}</h1> - is a JavaScript expression. It evaluates to a value, just like 1 + 2 or name.toUpperCase() or [1, 2, 3] do. The value it evaluates to is a JavaScript object that describes a tree of elements.
You can store that value in a variable (const greeting = ...). You can pass it to a function. You can return it from a function. You can put it in an array. It's a value.
This is the first thing that makes JSX feel weird and the first thing that needs to be unlearned. JSX is not "markup with some JavaScript escapes." It's the other way around: it's JavaScript with some XML-like syntax for describing trees. The XML is what gets transformed; the JavaScript surrounds it and dominates.
Let me write the same component with and without JSX, side by side, so you can see what I mean:
// With JSX:
function Greeting({ name }: { name: string }) {
return <h1 className="title">Hello, {name}</h1>
}
// Without JSX (same thing):
import { createElement } from 'react'
function Greeting({ name }: { name: string }) {
return createElement('h1', { className: 'title' }, 'Hello, ', name)
}
Both versions do exactly the same thing. The compiler turns the first into the second. The function call createElement(...) is what JSX actually is, mechanically. The angle brackets and slashes are sugar; under the sugar is a function call.
This is the operational fact I want you to internalize: every JSX expression is a function call in disguise. When you read <h1 className="title">Hello</h1>, see createElement('h1', { className: 'title' }, 'Hello') underneath. When you read a tree of nested JSX elements, see nested function calls. The pattern works for every JSX expression you'll ever encounter.
We'll look at the actual compiler output in detail in 3.2. For now, just hold the model: JSX compiles to function calls.
Why It Looks Like HTML
If JSX is JavaScript, why does it look like HTML?
Because the engineers building UIs in 2013 were used to HTML. Markup for UI was a familiar language. React's authors decided that the cost of inventing a new syntax for "describe a tree of UI elements" was higher than the cost of borrowing HTML's syntax. They invented a syntax extension to JavaScript that lets you write <h1>...</h1> and have it mean "this expression evaluates to an h1 element description." The choice of HTML-like syntax was pragmatic, not philosophical. Familiar wins.
The cost was real. JSX has been controversial since the moment it shipped (Lesson 1.2 covered the initial reception). Engineers from a templating background see <h1> and assume HTML rules apply: class="foo" should work, <input> shouldn't need closing, for="id" should be valid. None of those work, because JSX is JavaScript with HTML-ish syntax, not HTML. The mental adjustment takes time.
The benefit was also real. By making the markup an expression, JSX gets:
Type safety. The compiler checks that <h1> is valid, that prop names match the component's signature, that you're not passing a number where a string is expected. With templates in separate files, you'd lose all of that. With JSX, your editor and compiler can catch errors statically.
Composition. JSX is an expression, so you can compose it like any other expression - store it in variables, pass it as a function argument, return it conditionally. Templates can't do this. The flexibility is enormous.
One file per component. The markup, the logic, the styles (often), and the types live together. The historical "separation of concerns" argument said this was wrong; the practical experience showed it was right (Module 1.2 covered the debate). One file per component is now the default everywhere.
These benefits earned JSX its place. Even ecosystems that initially resisted (Angular 2 considered JSX before sticking with templates; Vue offers JSX as an alternative to its template syntax) eventually had to grapple with it. Today, every major frontend framework either uses JSX or has a JSX-like alternative.
But for understanding, the key insight is that the HTML-looking syntax is borrowed. It looks like HTML for human convenience. It behaves like JavaScript because it is JavaScript.
What JSX Is Not
A few specific things JSX isn't, that beginners often assume it is:
JSX is not HTML
The attributes are not HTML attributes. class becomes className. for becomes htmlFor. Boolean attributes work differently. The casing is camelCase, not kebab-case. Self-closing tags are required for void elements. We'll cover every one of these in Lesson 3.3.
You don't write HTML when you write JSX. You write JavaScript that looks like HTML. The distinction is what makes the next lesson make sense.
JSX is not a templating language
A templating language (Handlebars, Mustache, EJS, Vue templates, Angular templates) is a string syntax that gets parsed at runtime by a template engine. The engine reads {{name}} or *ngIf="x" or whatever the syntax is, looks up the values, and produces a string of HTML.
JSX doesn't work that way. JSX is parsed at build time by the compiler. It's transformed into JavaScript function calls before your code ever runs. By the time the code reaches the browser, there's no JSX anymore - just JavaScript. The browser never sees the angle brackets.
This is a meaningful distinction. Templating languages have their own rules, their own escape syntax, their own debugging story. JSX has none of that - its rules are JavaScript's rules; its escape syntax is "leave the JSX context with {} and enter regular JavaScript"; its debugging is JavaScript debugging.
JSX is not magic
There's a temptation, when learning JSX, to treat the angle brackets as magic - to feel like something special is happening at runtime when JSX appears in your code. It isn't.
JSX is a syntax extension that exists at the language level. The Babel compiler (or esbuild, or SWC - whichever your toolchain uses) recognizes the JSX syntax and transforms it into function calls. After transformation, your code is plain JavaScript. There's no runtime parser, no template engine, no special interpreter. At runtime, your code runs the same way any JavaScript code runs.
If you opened the network tab of your browser's DevTools, looked at the JavaScript files your Vite dev server is serving, and searched them for angle brackets, you wouldn't find your JSX. You'd find the compiled function calls. The JSX existed at build time and disappeared in transformation.
Why This Mental Model Matters
You might wonder: if JSX is just sugar over function calls, why does this distinction matter? Couldn't you write React fluently without ever thinking about the underlying function calls?
You could, mostly. The day-to-day work doesn't usually require thinking about the compiled output. But three situations do:
Situation 1: Debugging weird JSX behavior. When something in your JSX behaves unexpectedly - a conditional doesn't render, a key warning appears, an object error throws at runtime - the explanation almost always involves the underlying JavaScript. If you think of JSX as HTML, the bug is opaque. If you think of JSX as function calls, the bug is usually obvious.
Situation 2: Reading error messages. React's error messages often reference the compiled output. "Objects are not valid as a React child" - that error comes from passing an object to a function call where React expected primitives, elements, or arrays. The error talks about what happens at runtime; if you only think about JSX, the error is confusing.
Situation 3: Advanced patterns. Higher-order components, render props, component composition patterns - all become much clearer when you think of JSX as expressions. A component that "takes another component as a prop and wraps it" is just a function that takes a function call result and returns a different function call. The JSX makes it readable; the underlying function-call model makes it understandable.
So the mental model isn't just academic. It's the difference between writing React confidently and writing React superstitiously - adding things because they worked once, deleting them because they didn't, without a clear model of why.
I want you confident, not superstitious. That's why we spend a whole module on JSX before going further.
A Tiny Confession
Honestly, when I first encountered JSX in 2018, I had the same reaction many engineers do: "wait, HTML in JavaScript? That feels wrong." I'd come through freeCodeCamp learning JavaScript and HTML as separate things, with clear "separation of concerns" framing - markup in .html files, logic in .js files. Seeing them mixed felt like a violation of something I'd been taught was important.
It took maybe a month of writing React for the aesthetic objection to dissolve. What changed was that I noticed I was no longer thinking about "the HTML" and "the JavaScript" as separate things. I was thinking about the component - its inputs, its state, its output. The component was the unit; the file format was incidental. Once I got there, the JSX syntax felt natural - exactly the right shape for the work I was doing.
I share this because if you're feeling the resistance now, you're in good company. Many strong engineers had the same initial reaction. The way through is to write code in it, not to read more arguments for it. By Module 5, the aesthetic question will have stopped occurring to you, replaced by the practical one - "how do I express what this component should do?" - which JSX answers cleanly.
What I Want You to Take From This Lesson
Six things.
One: JSX is JavaScript. That's the sentence. Repeat it until it's automatic.
Two: JSX compiles to function calls. Specifically, React.createElement(...) (or jsx(...) with the new transform; same idea).
Three: The HTML-looking syntax is borrowed for human convenience. It looks familiar; it behaves like JavaScript.
Four: JSX is not HTML, not a templating language, not magic. Each of those mental models causes specific confusion.
Five: At runtime, there is no JSX. It's compiled to JavaScript before the browser sees it.
Six: The mental model matters for debugging, error messages, and advanced patterns. It's not academic.
Try It
Exercise: Warmup (10 minutes). Take three small JSX expressions from your Counter component. For each, write the equivalent
createElementcall by hand. Don't worry about exact precision - try to see the transformation.
Exercise: Core (15 minutes). Open the Babel REPL. Paste a JSX snippet. Look at the compiled output. Compare to what you predicted.
Exercise: Core (20 minutes). Write a small component that conditionally renders one of two elements. Then write the same component using only
createElementcalls - no JSX at all. Notice which version is easier to read.
Exercise: Stretch (30 minutes). Read React docs - "Writing Markup with JSX". Note how the docs introduce JSX; compare to this lesson's framing.