Curly braces

Embed JavaScript expressions inside markup and attributes with { }.

Signature
<h1 title={expression}>{expression}</h1>

Parameters

ParameterTypeDescription
$expressionanyAny JavaScript expression. Statements (if, for, variable declarations) are not allowed — compute them above the return instead.

Return value

ReactNodeIn tag content, the value is rendered — strings and numbers as text, elements as elements, arrays element by element; null, undefined and booleans render nothing. In an attribute, the value is passed through as the prop.

Errors

Embedding a plain object as tag content throws "Objects are not valid as a React child"; the exception is passing one to a prop, which is fine.

Curly braces are the door from markup back into JavaScript. They work in exactly two places: as tag content, and immediately after the = of an attribute.

The two positions

<h1>{name}</h1> renders the variable. src={avatar} passes the variable; src="{avatar}" passes the literal eight-character string, because quotes always mean a string. Tag names cannot be interpolated — <{tag}> is invalid. Assign the component to a capitalised variable and write <Tag /> instead.

Double braces

style={{ margin: 20 }} is not special syntax. The outer pair opens the expression and the inner pair is an object literal, which is why any object prop looks the same: person={{ name: 'Ada' }}.

What trips people up

Only expressions fit. if, for and const are statements and must move above the return; ternaries, && and map() work because they evaluate to a value.

Whitespace between lines is collapsed away, so {" "} or a template literal is sometimes needed to keep a space.

Comments go inside braces too: {/* like this */}. A bare // comment would swallow the closing brace.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Interpolating a user's name into a heading
  • Computing an aria-label or title attribute from props
  • Passing an object or array prop to a child component
  • Formatting a number or date before displaying it

Frequently asked questions

Why is my attribute showing the variable name instead of its value?

Because it is wrapped in quotes: src="{avatar}" passes the literal string. Drop the quotes and write src={avatar} — braces replace the quotes rather than sitting inside them.

What do the double curly braces in style={{ }} mean?

The outer braces open a JSX expression and the inner ones are a plain JavaScript object literal. It is not special syntax, which is why any object-valued prop looks the same.

How do I write a comment inside JSX?

Put a block comment inside braces: {/* like this */}. A // comment cannot be used in the same place because everything to the end of the line, including the closing brace, would be commented out.

More examples

Computing values above the return

The for loop and the let could not appear inside braces, so they run before the return and only their results are embedded. Calling a formatting function inside the braces is fine — a call is an expression. Intl formatting is locale-dependent, so pinning the locale string keeps the output identical everywhere rather than following the visitor's browser.

Example · jsx
const PRICES = [12.5, 9, 40];

function formatEUR(cents) {
  return new Intl.NumberFormat("de-DE", {
    style: "currency",
    currency: "EUR"
  }).format(cents);
}

export default function Total() {
  // Statements live here, not inside the braces.
  let total = 0;
  for (const price of PRICES) {
    total += price;
  }
  const label = total > 50 ? "Free shipping" : "Add more for free shipping";

  return (
    <div>
      <p>Total: {formatEUR(total)}</p>
      <p>{label}</p>
    </div>
  );
}

Spreading props, and dynamic tag names

Two more things the braces do. {...rest} spreads an object into props, which is object spread inside a JSX expression rather than new syntax. And because a tag name cannot be interpolated, the element type is put in a capitalised local variable — lowercase Tag would be treated as a literal <tag> element, which is the usual reason this pattern silently renders an unknown element.

Example · jsx
const HEADINGS = { 1: "h1", 2: "h2", 3: "h3" };

function Heading({ level, children, ...rest }) {
  // A capitalised variable, because <{tag}> is not valid JSX.
  const Tag = HEADINGS[level] ?? "h3";
  return <Tag {...rest}>{children}</Tag>;
}

export default function Article() {
  const props = { id: "intro", className: "lead" };
  return (
    <article>
      <Heading level={1} {...props}>
        Curly braces
      </Heading>
      <Heading level={2} data-section="basics">
        Two positions
      </Heading>
    </article>
  );
}

Discussion

  • Be the first to comment on this lesson.