Command Palette

Search for a command to run...

Hectal
PHASE 1Beginner ~20 min· topic 4 of 7

Topic 1.4

What JSX Can Render

In one line

Inside JSX, strings and numbers become text, elements become DOM, arrays render each item, and null, undefined, true and false render nothing; but 0 renders as "0", and plain objects crash with "Objects are not valid as a React child".

Think of it like this

A photo printer. Hand it a photo or a typed note and it prints it. Hand it a blank sheet and it prints nothing, no problem. Hand it a whole box of loose things (a sock, keys, a shoe) and it jams: it doesn't know which part to print. React is the printer, and the things you put in braces are what you hand it.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Child
Whatever you put between an element's opening and closing tags.
React node
Any value React can show as a child: string, number, element, array, null, undefined, boolean.
Falsy
A value treated as false in a condition: false, 0, "", null, undefined, NaN.
&& (logical AND)
a && b gives b if a is truthy, otherwise gives a itself.
Ternary
cond ? a : b: a when cond is truthy, b otherwise.
Plain object
A { key: value } collection. React can't render one directly.
Error boundary
A component that catches render errors below it and shows a fallback instead of a blank page.

Step by step

01Text and numbers become text

<p>{"Hi"} {42}</p> shows Hi 42. Numbers are turned into strings for you.

02Arrays render each item

<p>{["a", "b", "c"]}</p> shows abc. An array of elements shows each element. This is how lists work: items.map(...) returns an array of elements.

03Some values render nothing

null, undefined, true and false leave no trace. That makes condition && <X /> and condition ? <X /> : null work as "show only if".

Some values render nothingdiagram
Rendering diagram…

04Watch out for 0

{count && <Badge />} with count = 0 evaluates to 0, and 0 is a number, so the page shows 0. Turn the left side into a true boolean.

Inbox.jsxwhole filejsx
{count && <span className="badge">{count}</span>}      // shows 0 when count is 0
{count > 0 && <span className="badge">{count}</span>}  // shows nothing when 0

05Objects crash the render

Render a field or a formatted string, never the whole object. The error is thrown during rendering, and with no error boundary React removes the whole app from the page.

Try it yourself

  1. 1

    Fix the crash

    In the third example, change {user} to {user.name} from {user.city}. Predict the console: empty, and the page shows Logged in as Asha from Pune.

  2. 2

    Three ways to fix the badge

    In Inbox, try {!!count && …} and then {count ? <span className="badge">{count}</span> : null}. Both make the 0 disappear. Which reads most clearly to you?

  3. 3

    Add more rows

    In the first example, add ["zero", 0] and ["nested array", [1, [2, 3]]] to rows. Predict the cells before running. (0 shows 0; nested arrays flatten to 123.)

Code & diagrams

Every kind of child, side by side New tab

Strings, numbers, elements and arrays show up; `null`, `undefined`, `true`, `false` and `""` leave the cell empty. `NaN` prints `NaN`, and React warns about it first (the first console line) because it's almost always a bug in a calculation.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Received NaN for the `children` attribute. If this is expected, cast the value to a string.
a string -> "Hello"
a number -> "42"
an element -> "<b>bold</b>"
an array -> "abc"
null -> ""
undefined -> ""
true -> ""
false -> ""
empty string -> ""
NaN -> "NaN"
The `0 &&` gotcha and its fix New tab

The second buggy row prints `Inbox0`: `0 && …` evaluates to `0`, and React shows numbers. `count > 0` is a real boolean, so the fixed row shows nothing when the count is zero.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

"Inbox3"
"Inbox0"
"Inbox3"
"Inbox"
An object as a child crashes the render New tab

The page stays blank and React explains why: it found an object with keys `name` and `city`. Fix it by writing `{user.name}` (or `{user.name} from {user.city}`).

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Uncaught Error: Objects are not valid as a React child (found: object with keys {name, city}). If you meant to render a collection of children, use an array instead.
An error occurred in the <p> component.

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://react.dev/link/error-boundaries to learn more about error boundaries.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Render a Date

In the third example, change the paragraph to <p>Joined {new Date("2024-01-15T10:30:00Z")}</p>.

terminal
$ Console
── what you'll see ──
Uncaught Error: Objects are not valid as a React child (found: [object Date]). If you meant to render a collection of children, use an array instead.
An error occurred in the <p> component.

Break #2

Pass the component instead of using it

Write a component function Hello() { return <b>hi</b>; } and render <div>{Hello}</div>.

terminal
$ Console
── what you'll see ──
Functions are not valid as a React child. This may happen if you return Hello instead of <Hello /> from render. Or maybe you meant to call this function rather than return it.
<div>{Hello}</div>

Myth vs fact

Myth

false and 0 behave the same in JSX.

Fact

Both are falsy, but false renders nothing and 0 renders the text 0. That difference is the 0 && bug.

Myth

React prints objects like console.log does.

Fact

It throws "Objects are not valid as a React child". Pick the field you want or format the object into a string.

Myth

undefined in JSX shows the word undefined.

Fact

undefined (like null, true and false) renders nothing. That's why a missing field often shows a blank instead of an error.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    The ReactNode type in @types/react lists what may be a child: ReactElement | string | number | bigint | Iterable<ReactNode> | ReactPortal | boolean | null | undefined plus promises of those (for use and server components). Any iterable, not only arrays, works: a Set or a generator of elements renders too.

  • ▸

    Lint rules such as react/jsx-no-leaked-render catch the 0 && bug automatically by flagging && with non-boolean left sides in JSX.

  • ▸

    React deliberately renders true as nothing (not the string "true") so that cond && <X /> works. To show a boolean, convert it: {String(isAdmin)}.

Remember this

  1. 1

    A value placed as a child (between tags) is called a React node (in TypeScript, ReactNode). React knows how to show these: strings become text; numbers become text (42 → "42"); React elements (JSX) become DOM elements; arrays render every item in order, so {["a", "b", "c"]} shows abc and an array of elements shows each one (arrays of elements need keys, Topic 3.3).

  2. 2

    Four values render nothing at all: null, undefined, true and false. The empty string "" also shows nothing (it creates an empty text). This is useful: {isLoggedIn && <Logout />} shows the button when the condition is true, and when it is false React renders nothing. A component can return null to show nothing (Phase 3).

  3. 3

    **The 0 && gotcha.** a && b returns a when a is falsy. false && x is false, which renders nothing, but 0 && x is 0, a number, which renders as "0". So {count && <Badge />} shows a stray 0 when the count is zero. NaN behaves the same and renders NaN (React also warns about it). The fix: make the left side a real boolean: {count > 0 && <Badge />}, {!!count && …}, or use a ternary {count ? <Badge /> : null}.

  4. 4

    Plain objects can't be rendered. {user} where user = { name: "Asha" } throws `Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead. React can't guess which field you want. Render a field ({user.name}), a formatted string ({JSON.stringify(user)} while debugging), or map an array of objects to elements. A Date is an object too: it fails with (found: [object Date]); use date.toLocaleDateString()` or similar.

  5. 5

    Functions aren't valid children either: writing {Hello} instead of <Hello /> passes the function itself. React doesn't throw for this one; it renders nothing and warns `Functions are not valid as a React child. This may happen if you return Hello instead of <Hello /> from render.. A bigint (like 10n) renders as text too, and Promises have their own path through use` (Phase 14), but ordinary objects never render.

  6. 6

    Thrown errors while rendering unmount the whole tree unless an error boundary catches them (Topic 14.5). That's why an object-as-child bug blanks the entire page, not just one paragraph.

Explain it without notes

01

Which values render as nothing in JSX, and why is that useful?

02

Explain the 0 && bug and three ways to fix it.

03

What does "Objects are not valid as a React child" mean, and how do you fix it?

04

What's the difference between {Hello} and <Hello />?

Practice

01

Write a Cart component with a count prop that shows Cart plus a badge with the number only when count is above 0. Test it with 0 and 5.

02

Given const order = { id: 7, total: 450 }, render Order #7: ₹450 without crashing.

Trade-offs

  • ↔

    && is short but has the 0 trap; a ternary with null is longer but always safe. Many teams prefer the ternary (or a lint rule) for anything that isn't already a boolean.

Done when you can

  • I know which values render as text, as elements and as nothing.

  • I can spot and fix the 0 && bug.

  • I can read and fix "Objects are not valid as a React child".

  • I render Date objects as formatted strings.

  • I know {Hello} and <Hello /> are different.