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 && bgivesbifais truthy, otherwise givesaitself.- 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".
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.
{count && <span className="badge">{count}</span>} // shows 0 when count is 0
{count > 0 && <span className="badge">{count}</span>} // shows nothing when 005Objects 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
Fix the crash
In the third example, change
{user}to{user.name} from {user.city}. Predict the console: empty, and the page showsLogged in as Asha from Pune. - 2
Three ways to fix the badge
In
Inbox, try{!!count && …}and then{count ? <span className="badge">{count}</span> : null}. Both make the0disappear. Which reads most clearly to you? - 3
Add more rows
In the first example, add
["zero", 0]and["nested array", [1, [2, 3]]]torows. Predict the cells before running. (0shows0; nested arrays flatten to123.)
Code & diagrams
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
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 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
Console
Nothing logged yet.Expected console output
"Inbox3"
"Inbox0"
"Inbox3"
"Inbox"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
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>.
Break #2
Pass the component instead of using it
Write a component function Hello() { return <b>hi</b>; } and render <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
ReactNodetype in@types/reactlists what may be a child:ReactElement | string | number | bigint | Iterable<ReactNode> | ReactPortal | boolean | null | undefinedplus promises of those (foruseand server components). Any iterable, not only arrays, works: aSetor a generator of elements renders too. - ▸
Lint rules such as
react/jsx-no-leaked-rendercatch the0 &&bug automatically by flagging&&with non-boolean left sides in JSX. - ▸
React deliberately renders
trueas nothing (not the string"true") so thatcond && <X />works. To show a boolean, convert it:{String(isAdmin)}.
Remember this
- 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"]}showsabcand an array of elements shows each one (arrays of elements need keys, Topic 3.3). - 2
Four values render nothing at all:
null,undefined,trueandfalse. 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 isfalseReact renders nothing. A component canreturn nullto show nothing (Phase 3). - 3
**The
0 &&gotcha.**a && breturnsawhenais falsy.false && xisfalse, which renders nothing, but0 && xis0, a number, which renders as "0". So{count && <Badge />}shows a stray0when the count is zero.NaNbehaves the same and rendersNaN(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
Plain objects can't be rendered.
{user}whereuser = { 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. ADateis an object too: it fails with(found: [object Date]); usedate.toLocaleDateString()` or similar. - 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.. Abigint(like10n) renders as text too, and Promises have their own path throughuse` (Phase 14), but ordinary objects never render. - 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
Which values render as nothing in JSX, and why is that useful?
Explain the 0 && bug and three ways to fix it.
What does "Objects are not valid as a React child" mean, and how do you fix it?
What's the difference between {Hello} and <Hello />?
Practice
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.
Given const order = { id: 7, total: 450 }, render Order #7: ₹450 without crashing.
Trade-offs
- ↔
&&is short but has the0trap; a ternary withnullis 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
Dateobjects as formatted strings.I know
{Hello}and<Hello />are different.