skip to content

JSX Syntax and Semantics

JSX looks like HTML but is plain JavaScript expressions in disguise. This group covers what the syntax compiles to and the everyday semantics built on it — conditionals, lists, and fragments — which is where most 'why does this render nothing?' questions come from.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

15

In JSX, what does React render for `{value}` when value is a string, a number, true, null, undefined, an array, or a plain object?

level: juniorimportance: must knowfreq 62%

answer

  1. React dispatches on the child's type
  2. only four values are invisible
  3. numbers and strings both become text
  4. arrays flatten into the child list
  5. objects fail loudly, on purpose

basics

~20 s

Strings and numbers render as text. Arrays render each element in order. Null, undefined, true and false render nothing at all. A plain object is not a valid child and React throws an error instead of rendering it.

solid answer

~40 s

React classifies a child by its type. Strings and numbers become text nodes — including `0` and `NaN`, which is what makes falsy guards leak. Arrays are rendered element by element, so `{[a, b]}` behaves like writing the two children in sequence. `null`, `undefined`, `true` and `false` render nothing; they are the only values that disappear, which is why `return null` is the idiomatic "render nothing". A plain object is rejected — React raises an error saying objects are not valid as a React child — so you have to pick a field, format it, or serialise it yourself. That last case is the everyday bug: rendering `{user}` instead of `{user.name}`, or dropping a `Date` or an `Error` straight into the tree.

code

jsx · 13 lines
jsx
function Values() {
  return (
    <div>
      <p>[{'text'}]</p>
      <p>[{42}]</p>
      <p>[{0}]</p>
      <p>[{true}]</p>
      <p>[{null}]</p>
      <p>[{undefined}]</p>
      <p>[{['a', 'b']}]</p>
    </div>
  );
}

go deeper

for a junior

Memorise the short invisible list — null, undefined, true, false — and know that everything else you can see is a string or a number. Rendering an object is an error, so reach for a field of it.

for a middle

State the rule as type dispatch rather than truthiness, and derive the consequences on the spot: why return null works, why a numeric guard leaks, why an array needs no wrapper.

for a senior

Show how you keep the object-child error out of production — formatting at the edge where data enters, so components receive display-ready primitives rather than raw API objects or Error instances.

for a principal

Treat it as a boundary-design question: decide where raw domain objects get converted to renderable primitives, and make that layer explicit so the failure cannot re-enter through every new component that touches the same payload.

## Braces embed a value, and React decides what to do with it `{expr}` inside JSX means "evaluate this JavaScript expression and use the result as a child". The braces impose no filtering of their own — whatever the expression produces is handed to React, which then dispatches on the value's type. Knowing that table is the whole of conditional rendering in React, because every conditional idiom ends up producing one of these values. ## The table **Strings** render as a text node, exactly as typed. `{'Ada'}` and the literal text `Ada` in JSX are the same output. An empty string produces an empty text node — no visible output, but a node all the same. **Numbers** render as text too. `{42}` renders `42`, `{0}` renders `0`, and `{NaN}` renders `NaN`. There is no special case for zero, which is precisely why a falsy guard can print a stray digit. **null and undefined** render nothing. No node, no placeholder, no whitespace. **Booleans** render nothing — both `true` and `false`. This is a deliberate convenience so that boolean-producing guards can be written inline without emitting the word `false`. **Arrays** are flattened into the child list: each element is rendered in order, using these same rules recursively. `{['a', 'b']}` renders `ab`; `{[null, 'a']}` renders `a`. **React elements** — what JSX itself produces — render as the subtree they describe. **Plain objects** are not valid children. React raises an error stating that objects are not valid as a React child, naming the object's keys to help you find it. The same applies to a `Date`, a `Map`, or an `Error` instance: they are objects, so they are rejected rather than stringified. ## Why the object case is the one you actually hit Almost every occurrence in a real codebase is the same slip: the data arrived as an object and you rendered the whole thing. ```jsx // throws: objects are not valid as a React child <p>{user}</p> // fine <p>{user.name}</p> <p>{user.createdAt.toLocaleDateString()}</p> <p>{String(error)}</p> ``` Notice that React deliberately does *not* fall back to `String(value)` for objects. Doing so would render `[object Object]` on the page — a silent, useless output — where an error at least points at the line. It is a design decision to fail loudly on the case where the developer almost certainly made a mistake, and to render quietly on the cases (null, booleans) that are almost certainly intentional. ## Consequences you can reason out from the table Once you know the table, several React behaviours stop needing separate memorisation: - `return null` from a component renders nothing, because `null` renders nothing as a child. - `{isReady && <Panel />}` is safe when `isReady` is a real boolean, because the falsy branch produces `false`, which is invisible. - `{count && <Badge />}` is unsafe, because the falsy branch produces the number `0`, and numbers are visible. - Mapping an array of elements works with no wrapper, because arrays are flattened into the child list. - A stray `[object Object]` on the page is *not* a React default; if you see one, someone called `String()` or template-interpolated the object themselves. ## Whitespace, the near neighbour JSX collapses whitespace at the start and end of lines and drops lines that are pure whitespace, so two elements on separate source lines render with no space between them. Because a string child renders literally, the standard fix is to embed one: `{' '}`. It is the same rule — a string expression becomes a text node — applied to a formatting problem. ## What an interviewer is checking They want to see whether you hold a *rule* or a bag of anecdotes. Candidates who have only memorised idioms say "falsy things don't render", which is wrong in exactly the case that matters. Candidates who know the type table can derive the `0` leak, the object error, and the safety of `return null` on the spot, without having met each one before.

  • Why does React throw on an object child instead of just rendering `[object Object]`?
    Because rendering it would be silently useless. An object child is nearly always a mistake — the developer meant a field of it — so React fails loudly and names the object's keys, which points straight at the offending line. Stringifying would put meaningless text on the page and leave the bug to be found by a user.
  • Two JSX elements sit on separate source lines and render with no space between them. Why, and how do you add one?
    JSX trims leading and trailing whitespace on each line and removes lines that are entirely whitespace, so the newline between them contributes nothing. Embed an explicit string child — `{' '}` — between them, or put the space inside one of the elements' text. It works because a string expression always renders as a literal text node.
  • What renders for `{[null, 'a', 0, false]}`?
    The text `a0`. The array is flattened into the child list and each element follows the normal rules: `null` and `false` render nothing, the string `a` and the number `0` each render as text. Nothing about being inside an array changes how a value is treated.

saying these in an interview costs you the question

  • Says falsy values are skipped, so 0 is skipped
  • Expects an object child to render [object Object]
  • Thinks React prints the word false for boolean children
  • Believes an array child needs a wrapper element
  • Assumes a Date renders as a formatted date

context

open as a page

In React, what is a Fragment, and what problem does returning `<>...</>` from a component solve that a wrapping `<div>` does not?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A Fragment groups several children under one JSX parent while emitting no DOM node. A component can return multiple elements without an extra div that would distort CSS layout, break sibling and child selectors, invalidate HTML nesting, and add noise to the accessibility tree.

open as a page

In React, how do you render an array of items in JSX, and why does React log the warning "Each child in a list should have a unique key prop"?

level: juniorimportance: must knowfreq 85%

basics

~20 s

React renders a list by putting an array of elements in a JSX slot, normally items.map(item => <Row key={item.id} />). The key prop gives each element an identity independent of its position, so React can match it to the same item on the next render.

open as a page

In JSX, why must a component tag be written `<Profile />` rather than `<profile />`, and what does React actually do if you lowercase it?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Capitalization tells the JSX compiler whether the tag is a value or a string. A capitalized tag compiles to a reference to the variable of that name; a lowercase tag compiles to the string "profile", so React renders a literal DOM tag and your component never runs.

open as a page

In a React component, `{cart.length && <CartSummary />}` puts a stray `0` on the page when the cart is empty. Why does that happen, and how do you fix it?

level: middleimportance: must knowfreq 78%

basics

~20 s

JavaScript's && returns its left operand when that operand is falsy, so an empty cart makes the expression evaluate to the number 0. React skips null, undefined and booleans as children but renders numbers as text, so the 0 appears. Guard with cart.length > 0 instead.

open as a page

A React list is rendered as `rows.map((row, i) => <Row key={i} row={row} />)` and each Row contains an uncontrolled <input>. After the user deletes the first row, the remaining rows show the wrong typed text. What causes this, and what is the fix?

level: middleimportance: must knowfreq 78%

basics

~20 s

Index keys make position the identity. Deleting the first row shifts every later row down one index, so React sees the same keys 0..n-1 and reuses each existing Row and DOM node for a different item, leaving the old input text behind. Key by a stable item id instead.

open as a page

In a React project, what JavaScript does the JSX expression `<Button color="blue">Save</Button>` compile to, and what does that compiled call return at runtime?

level: middleimportance: must knowfreq 72%

basics

~20 s

JSX is sugar for a function call. With React's automatic runtime it compiles to jsx(Button, { color: 'blue', children: 'Save' }), imported from react/jsx-runtime. That call returns a plain, immutable JavaScript object describing what to render, not a DOM node.

open as a page

A React component must show a loading spinner, an error message, or a data table depending on its state. How do you structure that branching in JSX, and why are deeply nested ternaries discouraged?

level: middleimportance: should knowfreq 52%

basics

~20 s

Use early returns above the JSX, one per state, so each branch reads as a flat guard clause. JSX braces hold expressions, not statements, so an inline if is impossible; nested ternaries are the workaround people reach for, and they scale badly in review and in diffs.

open as a page

In React, you map a list of glossary entries to a `<dt>` and a `<dd>` pair inside one `<dl>`. Why can the `<>...</>` shorthand not be used here, and what do you write instead?

level: middleimportance: should knowfreq 44%

basics

~20 s

Each mapped entry produces one child of the list, so it needs a key — and the <>...</> shorthand accepts no attributes at all, key included. Write the explicit form instead: <React.Fragment key={entry.id}> wrapping the <dt> and <dd>.

open as a page

In React, does a key have to be unique across the whole page, and can the child component read the value you passed as its key?

level: middleimportance: should knowfreq 48%

basics

~20 s

A key only has to be unique among its siblings — the children of one parent in one array — so two separate lists may both use keys 1, 2, 3. The child cannot read it: React consumes key and never forwards it in props.

open as a page

Files containing JSX no longer need `import React from 'react'` at the top. What changed in the compiled output to make that possible, and when do you still need to import React?

level: middleimportance: should knowfreq 54%

basics

~20 s

The automatic JSX runtime, added in React 17, makes the compiler inject its own import of jsx from react/jsx-runtime instead of emitting React.createElement. You still import React when your code names a React API directly, such as React.Component or React.memo.

open as a page

In React, what actually changes if you write `{isOpen && <Panel />}` instead of always rendering `<Panel hidden={!isOpen} />`, and how do you choose between them?

level: seniorimportance: should knowfreq 44%

basics

~20 s

The conditional version removes Panel from the tree, so it unmounts: its state and refs are discarded and its effect cleanups run, and reopening remounts it fresh. The hidden version keeps it mounted, preserving state and DOM but paying render and memory cost while invisible.

open as a page

A `<Fields>` React component wraps the inputs it returns in a `<div>` so that it can return several elements. Once it is placed inside a `display: grid` form container, everything it renders lands in a single grid cell instead of flowing across the grid's columns. Explain the mechanism and the fix.

level: seniorimportance: should knowfreq 36%

basics

~20 s

Grid and flex containers lay out only their direct children, and the wrapper div is now the sole direct child, so it becomes one grid item holding everything. Replace the wrapper with a fragment so the inputs become direct children of the grid again.

open as a page

You render a React list from an API response whose items have no id field. What do you use as the key, and why is `key={crypto.randomUUID()}` written inline in the JSX the wrong answer?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Derive a key from fields that are stable and unique together, or mint an id once when the data enters the app and store it with the item. Generating one inline in JSX produces a new key every render, so React unmounts and remounts the whole list each time.

open as a page

Given `function Row({ item }) { ... }`, does the line `const el = <Row item="a" />;` call Row? Explain what that assignment actually creates in React.

level: seniorimportance: should knowfreq 44%

basics

~20 s

No — nothing calls Row. The assignment creates a React element: a plain, immutable object recording that Row should be called with { item: "a" }. React invokes the component itself while rendering, so an element can be created, stored, or thrown away without the component ever running.

open as a page