C.W.K.
Stream
Lesson 01 of 07 · published

Components Are Functions, JSX Is Sugar

~14 min · components, jsx, fundamentals

Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
If you remember nothing else: a React component is a function that returns elements. JSX is the convenience that lets you describe those elements as HTML-shaped expressions instead of nested function calls.

Function in, element out

A function component takes one argument — its props object — and returns a React element (or a fragment, or null, or an array of elements). That's the entire contract. No class inheritance, no lifecycle methods you have to override, no this.

React calls your function whenever it needs to know what your component would render right now. The output is a description of the UI, not the UI itself. React's reconciler compares descriptions and decides which DOM mutations to perform.

JSX desugars to function calls

JSX is not part of JavaScript. The Vite + React plugin (via Babel or SWC under the hood) transforms JSX into jsx() / jsxs() calls from react/jsx-runtime. You almost never write those calls directly, but knowing they exist demystifies error messages that mention them.

What a fragment is

<>...</> (the empty-tag shorthand) is a fragment. It groups children without adding a DOM wrapper. Use it when a component needs to return multiple sibling elements and a parent <div> would pollute the layout.

The conditional patterns

Three idioms cover 95% of conditional rendering:

  • {condition && <Foo />} — render Foo only when condition is truthy.
  • {condition ? <A /> : <B />} — pick one.
  • {items.map(item => <Row key={item.id} {...item} />)} — render a list.

The list one needs a key. The key tells React's reconciler 'this element corresponds to that previous element' so it can move/update instead of remount.

Don't use array indexes as keys unless the list is static. If items reorder, get deleted, or get inserted in the middle, index keys cause React to associate the wrong DOM nodes with the wrong items — and you'll see input state bleeding from one row to another. Use a stable id from your data.

The 0/false rendering pitfall

{count && <Badge />} when count === 0 renders the literal number 0 in the DOM (because 0 is falsy and short-circuit returns 0, and React renders numbers as text). Use {count > 0 && <Badge />} or {Boolean(count) && <Badge />} instead.

Code

The same component as JSX and as desugared jsx() calls·tsx
// What you write
function Hello({ name }: { name: string }) {
  return (
    <div className="greeting">
      <h1>Hello, {name}</h1>
      <p>Welcome</p>
    </div>
  );
}

// What the Vite plugin emits (roughly)
import { jsx, jsxs } from "react/jsx-runtime";

function Hello({ name }: { name: string }) {
  return jsxs("div", {
    className: "greeting",
    children: [
      jsx("h1", { children: ["Hello, ", name] }),
      jsx("p", { children: "Welcome" }),
    ],
  });
}
All three conditional patterns in one component·tsx
type Item = { id: string; label: string };

function List({ items, loading, error }: {
  items: Item[];
  loading: boolean;
  error: string | null;
}) {
  // Ternary: choose between two views
  if (loading) return <p>Loading…</p>;

  // && short-circuit: render only when truthy
  return (
    <>
      {error && <p className="text-red-500">{error}</p>}
      {items.length > 0 ? (
        <ul>
          {items.map((item) => (
            // map + stable key
            <li key={item.id}>{item.label}</li>
          ))}
        </ul>
      ) : (
        <p>No items yet.</p>
      )}
    </>
  );
}

External links

Exercise

Write a Stats component that takes { total: number; pending: number; errors: number }. Use a fragment to return three <p> siblings. For each, use a conditional that hides the line when the count is 0. Render <Stats total={5} pending={0} errors={2} /> and verify only the total and errors lines appear.
Hint
Watch out for the 0-rendering pitfall. {pending && <p>...</p>} would render literal 0 when pending is 0. Use {pending > 0 && <p>...</p>}.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.