JSX Fundamentals
JSX is a function call in disguise - attributes, nesting, and self-closing tags.
What you will be able to do
- Explain what JSX is, and what it compiles to
- Write the same element as JSX and as createElement
- Name the differences from HTML - className, htmlFor, camelCase events
- Close every element, and know why <img> and <br> need a slash
- Put JavaScript values into markup and attributes with {}
- Tell a DOM element from a component by its first letter
- Return several elements under one parent, and write a JSX comment
The idea, in plain English
JSX is a syntax extension that lets you write HTML-like markup inside JavaScript or TypeScript. It is not HTML, and browsers cannot run it. Before your code reaches the browser, the compiler - Vite, in this course - turns every JSX tag into an ordinary JavaScript function call.
That single fact explains every JSX rule in this lesson. Tags must be closed because the compiler needs to know where each call ends. Attributes become the keys of a JavaScript object, so they follow JavaScript names - className, not class. A component returns one value, so several siblings need one parent. And anything inside curly braces is plain JavaScript, evaluated and passed along.
You will almost never write the function calls yourself. JSX exists because <h1>Hello React</h1> is far easier to read than the call it becomes - especially once elements start nesting.
Worked example: The same element written as JSX and as createElement.
JSX compiles to function calls
Write <h1>Hello React</h1> and the compiler produces React.createElement("h1", null, "Hello React"): the tag name, then an object of attributes (null when there are none), then the children.
Newer projects, including the Vite template, use the automatic JSX transform, which calls a function called jsx from react/jsx-runtime instead: jsx("h1", { children: "Hello React" }). The shape is different; the idea is the same - JSX is a readable way to write a call that describes an element.
What that call returns is a plain object describing what should be on screen. React reads those objects and updates the real DOM to match.
<h1>Hello</h1>React.createElement("h1", null, "Hello")<img src="/logo.png" alt="Logo" />React.createElement("img", { src: "/logo.png", alt: "Logo" })<div className="card">React.createElement("div", { className: "card" }, ...children)<p>{lessons} lessons</p>React.createElement("p", null, lessons, " lessons")<CourseCard />React.createElement(CourseCard, null) - the function itself, not a stringJSX is not HTML
JSX looks like HTML on purpose, and most of what you know carries over: the same tag names, the same nesting, similar attributes. But it lives inside your JavaScript, so it follows JavaScript rules wherever the two disagree.
Curly braces are the clearest example. In <h1>Hello {name}</h1>, the braces switch back to JavaScript, and the value of the variable name is placed in the heading. HTML has nothing like this - in an .html file, {name} would appear on screen literally.
Nesting, and the one-parent rule
Elements nest in JSX exactly as they do in HTML, and the indentation makes the tree easy to see: a div holding an h1 and a p is written as a div holding an h1 and a p.
A component returns one value. Two sibling tags side by side would be two values, so the compiler rejects them with "JSX expressions must have one parent element." Wrap them in a parent - a div if the wrapper means something, or an empty <>...</> Fragment if it does not. Fragments are Lesson 7.
Every element must be closed
HTML forgives an <img> or <br> with no closing tag. JSX does not: every element must be closed, either with a closing tag or with a slash at the end of the opening tag. <img src="/logo.png"> fails to compile with "JSX element img has no corresponding closing tag."
An element with no children is almost always written self-closing: <img />, <input />, <br />, <hr />. <img src="/logo.png"></img> is also valid JSX - it is just longer. Your own components follow the same rule, so <CourseCard /> is the normal way to use one with no children.
Attributes use JavaScript names
Because attributes become the keys of a JavaScript object, JSX uses the DOM property names rather than the HTML attribute names. class is a reserved word in JavaScript, so the attribute is className. for is also reserved, so a label uses htmlFor. Multi-word names are camelCase: tabIndex, onClick, onMouseEnter.
React writes the right HTML for you: className="card" becomes class="card" in the DOM, htmlFor becomes for, and tabIndex={1} becomes tabindex="1".
Getting it wrong fails in a way that is easy to miss. TypeScript flags class with "Did you mean className?". React in development logs "Invalid DOM property class". And a lowercase onclick is worse: React logs a warning and does not attach the handler at all, so the button silently does nothing.
class="card"className="card"for="email"htmlFor="email"tabindex="1"tabIndex={1}onclick="save()"onClick={save} - a function, not a string of code<img src="a.png"><img src="a.png" /><!-- comment -->{/* comment */}Strings in quotes, JavaScript in braces
An attribute value in quotes is a plain string: src="/logo.png". An attribute value in braces is a JavaScript expression: src={imageUrl}, disabled={isLoading}, tabIndex={1}. Numbers and booleans need braces - tabIndex="1" passes the string "1".
The same braces work between tags. {course.title}, {lessons} lessons, {100 + 92}, {name.toUpperCase()} - any expression that produces a value. Statements such as if and for are not expressions, so they cannot go inside braces; Lesson 6 covers what can, and Lesson 8 covers conditions.
Lowercase is a tag, uppercase is a component
JSX decides what a tag is from its first letter. A lowercase name - div, button, img - becomes a string, and React creates that DOM element. A capitalised name - CourseCard, Header - becomes a reference to your function, and React calls it.
So <courseCard /> does not call your component. It compiles to React.createElement("courseCard"), React creates an unknown <coursecard> element in the page, and logs "is using incorrect casing. Use PascalCase for React components." Component names always start with a capital letter.
Comments, whitespace, and parentheses
Between tags, a JSX comment goes inside braces: {/* Course heading */}. A // comment written between tags is not a comment at all - it is text, and it appears on the page.
JSX trims the whitespace at the start and end of each line, then joins the lines of text with a single space. Hello on one line and World on the next render as "Hello World". But two braces on separate lines are joined with nothing, so {first} and {last} on two lines render as "ChanduNandigam". When a space matters, keep it on one line - {first} {last} - or write it explicitly as {" "}.
Multi-line JSX after return is wrapped in parentheses. Without them, a line break straight after return ends the statement: the function returns undefined, the page shows nothing, and there is no error to tell you why.
Syntax and examples
// What you write
const heading = <h1 className="title">Hello React</h1>;
// What the classic transform produces
const heading2 = React.createElement("h1", { className: "title" }, "Hello React");
// What the automatic transform produces (the Vite template uses this)
import { jsx as _jsx } from "react/jsx-runtime";
const heading3 = _jsx("h1", { className: "title", children: "Hello React" });function Course() {
return (
<div>
<h1>React Development</h1>
<div>
<h2>Module 1</h2>
<p>React Fundamentals</p>
</div>
</div>
);
}
// div
// ├── h1
// └── div
// ├── h2
// └── pconst imageUrl = "/logo.png";
const isSaving = true;
function Form() {
return (
<form>
<img src={imageUrl} alt="Company logo" />
<label htmlFor="email">Email</label>
<input id="email" type="email" tabIndex={1} />
<button className="primary" disabled={isSaving}>
Save
</button>
</form>
);
}<form>
<img src="/logo.png" alt="Company logo">
<label for="email">Email</label>
<input id="email" type="email" tabindex="1">
<button class="primary" disabled="">Save</button>
</form>const course = {
title: "React Development",
lessons: 192,
};
const name = "react";
function Summary() {
return (
<div>
<h2>{course.title}</h2>
<p>{course.lessons} lessons</p>
<p>Total: {100 + 92}</p>
<p>{name.toUpperCase()}</p>
</div>
);
}
// React Development
// 192 lessons
// Total: 192
// REACTfunction Header() {
return <h1>My Learning Platform</h1>;
}
function App() {
return (
// div is lowercase: a DOM element that React creates
// Header is capitalised: your function, which React calls
<div>
<Header />
</div>
);
}return (<h1>Hello</h1><p>Welcome</p>);
-> JSX expressions must have one parent element.
<div><img src="/logo.png"></div>
-> JSX element 'img' has no corresponding closing tag.
<div class="container">
-> Property 'class' does not exist ... Did you mean 'className'?
<button onclick={save}>
-> Property 'onclick' does not exist ... Did you mean 'onClick'?function App() {
const first = "Chandu";
const last = "Nandigam";
return (
<div>
{/* This is a JSX comment - it renders nothing */}
// This is not a comment - it appears on the page
{/* Renders: Hello World */}
<p>
Hello
World
</p>
{/* Renders: Chandu Nandigam */}
<p>{first} {last}</p>
{/* Renders: ChanduNandigam - no space */}
<p>
{first}
{last}
</p>
</div>
);
}function CourseCard() {
const course = {
title: "React Development",
lessons: 192,
level: "Beginner",
};
return (
<article className="course-card">
<h2>{course.title}</h2>
<p>{course.lessons} lessons</p>
<p>Level: {course.level}</p>
<button>Start Learning</button>
</article>
);
}
export default CourseCard;Tip: When a JSX rule surprises you, ask what the function call would look like. Two roots would be two return values; class would be a reserved word as an object key; an unclosed <img> leaves the call without an end.
Watch out: A lowercase onclick compiles, renders, and does nothing - React drops it with only a console warning. TypeScript catches it; plain JavaScript does not.
JSX rules
The differences from HTML you will use every day.
One parentA component returns one element - wrap siblings in a div or <>...</>.
<> <h1 /> <p /> </>
Close everythingUse a closing tag, or end the opening tag with />.
<img src="a.png" />
classNameThe CSS class attribute.
<div className="card">
htmlForLinks a label to an input.
<label htmlFor="email">
camelCaseMulti-word attributes and all event handlers.
onClick tabIndex onMouseEnter
{ }A JavaScript expression, in content or in an attribute.
<p>{course.title}</p>Capital letterA component; lowercase is a DOM element.
<CourseCard />
{/* */}A comment between tags.
{/* note */}Try it yourself
The code does not change. Swap the content string and the program does something else entirely.
“Return <h1>Hello</h1> and <p>Welcome</p> side by side, read the compiler error, then fix it once with a div and once with <>...</>.”
“Write <img src="/logo.png" alt="Logo" /> as React.createElement by hand. Where do the attributes go?”
“Write <button onClick={() => alert("hi")}>, click it, then change onClick to onclick. The button still renders - what happens when you click it?”
“Render {first} and {last} on two separate lines, then on one line. Which one puts a space between the names?”
What usually goes wrong
class is a reserved word in JavaScript. TypeScript rejects it, and React in development warns "Invalid DOM property class".
✗ <div class="container">✓ <div className="container">HTML lets <img>, <input>, and <br> go unclosed. JSX requires every element to be closed.
✗ <img src="/logo.png">✓ <img src="/logo.png" />A component returns a single value, so sibling elements need one parent.
✗ return (
<h1>Hello</h1>
<p>Welcome</p>
);✓ return (
<>
<h1>Hello</h1>
<p>Welcome</p>
</>
);Without braces, a variable name is just text.
✗ <h1>name</h1> // shows: name✓ <h1>{name}</h1> // shows the value of nameLowercase tags are treated as DOM elements, so React creates an unknown <coursecard> element instead of calling your function.
✗ <courseCard />✓ <CourseCard />A // comment between JSX tags is rendered as text on the page.
✗ <div>
// heading
<h1>Courses</h1>
</div>✓ <div>
{/* heading */}
<h1>Courses</h1>
</div>Best practices
- Wrap multi-line JSX in parentheses after return.
- Self-close every element that has no children, including your own components.
- Use a div only when the wrapper means something; otherwise use a Fragment.
- Name components in PascalCase and keep DOM elements lowercase.
- Keep JSX readable - pull long calculations into a variable above the return.
- Let TypeScript check your attributes; most JSX typos become compile errors.
Practice
Write these yourself before opening anything. Getting them wrong first is most of how this sticks.
Rewrite this as JSX: React.createElement("div", { className: "card" }, React.createElement("h2", null, "React"), React.createElement("p", null, "192 lessons")).
Show hintHide hint
The first argument is the tag, the second the attributes, and the rest are children.
Show solutionHide solution
<div className="card">
<h2>React</h2>
<p>192 lessons</p>
</div>Fix every JSX error in this snippet.
Show hintHide hint
There are five problems - count the roots, the unclosed tags, and the HTML attribute names.
Show solutionHide solution
// Before
function Signup() {
return (
<label for="email" class="label">Email</label>
<input id="email" type="email">
<br>
);
}
// After
function Signup() {
return (
<>
<label htmlFor="email" className="label">Email</label>
<input id="email" type="email" />
<br />
</>
);
}Build a CourseCard from { title: "React Development", lessons: 192, level: "Beginner", duration: "30 hours" }, inside an <article> with className="course-card", ending with a Start Learning button.
Show hintHide hint
Every value on screen comes from the object through {}.
Show solutionHide solution
const course = {
title: "React Development",
lessons: 192,
level: "Beginner",
duration: "30 hours",
};
function CourseCard() {
return (
<article className="course-card">
<h2>{course.title}</h2>
<p>{course.lessons} lessons</p>
<p>Level: {course.level}</p>
<p>Duration: {course.duration}</p>
<button>Start Learning</button>
</article>
);
}
export default CourseCard;A course card from variables
Build a card where every piece of text comes from a variable, and style it through className.
- Use <article className="course-card"> as the root.
- Keep title, description, lessons, and level in variables above the component.
- Render them with an h2, p elements, and a Start Learning button.
- Add a .course-card rule in a CSS file and import it.
- Render the card twice from App to prove it is a reusable component.
import "./CourseCard.css";
const title = "React Development";
const description = "Learn React from fundamentals to advanced concepts.";
const lessons = 192;
const level = "Beginner";
function CourseCard() {
// return the card here
}
export default CourseCard;Show one solutionHide solution
// CourseCard.css
// .course-card { padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
import "./CourseCard.css";
const title = "React Development";
const description = "Learn React from fundamentals to advanced concepts.";
const lessons = 192;
const level = "Beginner";
function CourseCard() {
return (
<article className="course-card">
<h2>{title}</h2>
<p>{description}</p>
<p>Lessons: {lessons}</p>
<p>Level: {level}</p>
<button>Start Learning</button>
</article>
);
}
export default CourseCard;
// App.tsx
import CourseCard from "./CourseCard";
function App() {
return (
<main>
<CourseCard />
<CourseCard />
</main>
);
}Key points
- JSX is markup inside JavaScript; the compiler turns each tag into a function call.
- <h1>Hi</h1> becomes React.createElement("h1", null, "Hi") - or jsx() with the automatic transform.
- Attributes become object keys, so they use JavaScript names: className, htmlFor, onClick, tabIndex.
- Every element must be closed - with a closing tag or with />.
- A component returns one element; wrap siblings in a div or a Fragment.
- Curly braces hold JavaScript expressions, in content and in attributes.
- Lowercase tags are DOM elements; capitalised tags are components.
- Comments go in {/* */}; a // between tags is rendered as text.
- Wrap multi-line JSX in parentheses after return.
Quick check before you move on
Interview questions
What is JSX?
A syntax extension that lets you write HTML-like markup inside JavaScript. The compiler turns each element into a function call - React.createElement or jsx from react/jsx-runtime - which returns an object describing the element.
Is JSX HTML?
No. It resembles HTML, but it is JavaScript: attributes use DOM property names, every element must be closed, and braces embed JavaScript expressions.
Why className instead of class?
JSX attributes become keys in a props object and follow DOM property naming, where the property is className. class is also a reserved word in JavaScript. React writes class to the actual DOM.
Why must a component return a single root element?
Because the JSX compiles to one function call that returns one value. Two sibling tags would be two values. A Fragment groups them without adding a DOM element.
How does React tell a component from an HTML element in JSX?
By the first letter. Lowercase names compile to strings and become DOM elements; capitalised names compile to a reference to the component function, which React calls.
Can you put HTML in a string and render it with braces?
Not as HTML - React escapes strings, so the tags appear as text. That escaping is what protects against injection. Rendering raw HTML requires dangerouslySetInnerHTML, deliberately named to discourage it.
Quiz
- 1.
What is JSX? A. A database language B. HTML that browsers run directly C. A syntax extension for JavaScript and TypeScript D. A CSS framework
- 2.
Which is correct JSX? A. <div class="container"> B. <div className="container"> C. <div classname="container"> D. <div css="container">
- 3.
Which of these is NOT valid JSX? A. <img src="logo.png"> B. <img src="logo.png" /> C. <img src="logo.png"></img> D. <img />
- 4.
How do you render the value of name? A. <h1>name</h1> B. <h1>{name}</h1> C. <h1>[name]</h1> D. <h1>(name)</h1>
- 5.
Which is a React component? A. <div /> B. <button /> C. <CourseCard /> D. <input />
- 6.
What does <p>{"<b>bold</b>"}</p> show on the page? A. bold, in bold B. The text <b>bold</b>, tags and all C. Nothing D. An error
Comments
Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.
Loading comments...
AI
System Design
Backend
- GraphQL8 modules · 69 lessons planned
- Core Python13 modules · 75 lessons planned
- FastAPI5 sections · 20 lessons
- Node.js14 modules · 206 lessons planned
- Node.js Performance7 chapters · 36 topics
- Event Loop Lifecycle6 phases · 3 scenarios
- Docker & Containerization11 modules · 144 lessons planned
- AWS for Developers14 modules · 219 lessons planned
- CI/CD & DevOps Automation10 modules · 134 lessons planned