JSX and Components
JSX syntax rules, function components, props, and composing components together.
JSX is JavaScript, not HTML
JSX looks like HTML embedded in JavaScript, but it's actually syntactic sugar that compiles down to plain function calls. This:
const element = <h1 className="title">Hello!</h1>;
compiles to roughly this:
const element = React.createElement("h1", { className: "title" }, "Hello!");
Because JSX is just JavaScript underneath, it follows JavaScript's rules in places where HTML wouldn't, which is where most of JSX's "gotchas" come from.
Rule 1: a single root element
A component's JSX must return one root element — you can't return two sibling elements side by side:
// Wrong — two adjacent elements with no common parent
function Header() {
return (
<h1>Title</h1>
<p>Subtitle</p>
); // SyntaxError
}
Wrap them in a parent element instead. If you don't want an extra <div> cluttering your actual DOM output, use a Fragment (<>...</>), which renders nothing of its own:
function Header() {
return (
<>
<h1>Title</h1>
<p>Subtitle</p>
</>
);
}
Rule 2: className, not class
class is a reserved word in JavaScript, so JSX uses className for the HTML class attribute instead:
function Badge() {
return <span className="badge badge-success">Active</span>;
}
The same reasoning applies to a few other attributes: for (on a <label>) becomes htmlFor, and event attributes are camelCase (onclick becomes onClick).
Rule 3: embedding JavaScript expressions with {}
Curly braces drop you back into plain JavaScript inside otherwise-markup-looking code. Anything that's a valid JavaScript expression works — variables, function calls, ternaries, arithmetic:
function UserCard({ user }) {
const isAdult = user.age >= 18;
return (
<div className="card">
<h3>{user.name}</h3>
<p>Age: {user.age}</p>
<p>Status: {isAdult ? "Adult" : "Minor"}</p>
<p>Next year: {user.age + 1}</p>
</div>
);
}
{} only accepts expressions, not statements — {if (x) { ... }} is invalid JSX. That's why conditional rendering leans on ternaries (cond ? a : b) or the && short-circuit pattern rather than if blocks directly inside markup:
function Notification({ count }) {
return (
<div>
{count > 0 && <span className="badge">{count} new</span>}
</div>
);
}
Function components
A component is a JavaScript function whose name starts with a capital letter and which returns JSX. React treats a capitalized tag (<UserCard />) as a component reference, and a lowercase tag (<div />) as a plain HTML element — this distinction is why the capitalization rule from the introduction page matters so much.
function Greeting() {
return <p>Hello there!</p>;
}
Props: passing data into components
Props (short for "properties") are how a parent component passes data down into a child. They arrive as a single object argument, which is almost always destructured in the function signature:
function UserCard({ name, age, role }) {
return (
<div className="card">
<h3>{name}</h3>
<p>{age} years old — {role}</p>
</div>
);
}
function App() {
return (
<div>
<UserCard name="Ada Lovelace" age={28} role="Engineer" />
<UserCard name="Grace Hopper" age={34} role="Admiral" />
</div>
);
}
Notice age={28} uses curly braces because 28 is a JavaScript number, not a string — attributes that aren't plain string literals always need {}.
Props are read-only from the child's perspective. A component must never reassign or mutate a prop it received — if a value needs to change over time, it belongs in state instead (covered on the next page), and gets passed down as a prop from whichever component owns that state.
function Bad({ count }) {
count = count + 1; // never do this — props are read-only
return <p>{count}</p>;
}
Composing components
Real UIs are built by nesting components inside each other, the same way you nest HTML elements. A component can also receive other JSX as a prop via the special children prop, which is what makes wrapper/layout components possible:
function Card({ title, children }) {
return (
<div className="card">
<h3 className="card-title">{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
function App() {
return (
<Card title="Profile">
<p>Name: Ada Lovelace</p>
<p>Role: Engineer</p>
</Card>
);
}
Whatever is written between <Card> and </Card> is passed to Card as props.children — this is the same pattern behind layout components, modals, and any component that needs to wrap arbitrary content.
Common mistakes
- Returning multiple sibling elements without wrapping them in a single parent or
<>...</>fragment. - Writing
class="..."instead ofclassName="..."— it silently does nothing in React (the attribute is ignored, not an error). - Putting a JavaScript statement (
if,for) inside{}— only expressions are allowed there. - Mutating a prop directly instead of treating it as read-only input.
- Forgetting to destructure props and writing
props.nameeverywhere instead of{ name }in the function signature — not wrong, just less idiomatic and harder to read in larger components.