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:

Javascript
const element = <h1 className="title">Hello!</h1>;

compiles to roughly this:

Javascript
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:

Javascript
// 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:

Javascript
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:

Javascript
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:

Javascript
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:

Javascript
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.

Javascript
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:

Javascript
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.

Javascript
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:

Javascript
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 of className="..." — 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.name everywhere instead of { name } in the function signature — not wrong, just less idiomatic and harder to read in larger components.