cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

JavaScript in JSX with curly braces

Use curly braces in JSX to show values, run expressions, set attributes from variables, and pass style objects, with live React examples you can edit.

By the end, you can
  • 01
    Show values with bracesPut any JavaScript expression between tags, and say what React shows for text, numbers, booleans, null, arrays, and objects.
  • 02
    Tell an expression from a statementExplain why if and for can't go inside braces, and use a ternary or a variable worked out before the return instead.
  • 03
    Set attributes and styles from dataChoose between quotes and braces for an attribute, and write a style object with double curlies, camelCase names, and numbers in px.

A card that reads its own data

In Writing markup with JSX, every tea card had its words typed straight into the tags: <h3>Green tea</h3>. A real shop keeps its teas as data: names, prices, and how many are left. To show data, JSX has curly braces, { }. Here is a card that reads three values. The result is live:

A tea card that reads three valuesPop out in the code editor (opens in a new tab)JSX
const tea = "Green tea";const price = 3;const cups = 2; export default function TeaCard() {  return (    <article className="tea-card">      <h3>{tea}</h3>      <p>{cups} cups cost ${price * cups}</p>    </article>  );}
ResultLoading…

Lines 1 to 3 are ordinary JavaScript variables. Line 8 has braces around tea, so the heading shows the variable's value, Green tea, not the word "tea". Line 9 has two pairs of braces. The first shows cups, 2. The second works out price * cups, 3 times 2, and shows 6. The $ in front of it is plain text, like the word "cost". The result reads 2 cups cost $6.

Press Edit & run and change cups on line 3 to 3. The result says 3 cups cost $9: the braces are worked out again every time the component runs, from whatever the data is now.

A box named tea holds a card, Green tea. An arrow leads to a card with the JSX h3 with tea in curly braces. Another arrow leads to a page window showing the heading Green tea.Green teateayour variable<h3>{tea}</h3>braces in your JSXpageGreen teawhat React shows
The braces in <h3>{tea}</h3> switch from markup back to JavaScript. React reads tea when the component runs, and its value, Green tea, goes into the heading.
Real-life analogyA form letter with blanks

A shop sends the same letter to every customer, with blanks for the name and the order. The template never changes. Each time, the office fills the blanks from one customer's details, and only something that fits in a blank, like a name or a number, can go there.

In real life: A letter with blanks: "Dear ____, your order of ____ cups is ready"
In React: JSX with braces: <p>{cups} cups cost ${price * cups}</p>
In real life: The office fills each blank from the customer's details
In React: React works out each pair of braces when the component runs
In real life: Each blank takes a word or a number, never an instruction
In React: Each pair of braces takes an expression, never a statement
In real life: New details, new letters, same template
In React: New data, new result, same component
The main idea

Curly braces in JSX switch from markup back to JavaScript. Whatever you write inside them must be an expression, code that works out to a value. React works it out each time the component runs, and puts the value in that spot: as text between tags, or as an attribute's value.

Braces between tags show a value

Between tags, the braces can hold any expression, not only a variable: a sum, a method call, a property, an item from an array, or a call to your own function. This order summary uses each:

An order summary with four kinds of expressionPop out in the code editor (opens in a new tab)JSX
const customer = "Asha";const items = ["Green tea", "Lemon cake", "Iced tea"];const total = 11.5; function formatPrice(amount) {  return "$" + amount.toFixed(2);} export default function OrderSummary() {  return (    <section>      <h3>Thanks, {customer.toUpperCase()}!</h3>      <p>You ordered {items.length} items.</p>      <p>First up: {items[0]}</p>      <p>Total: {formatPrice(total)}</p>    </section>  );}
ResultLoading…
  • Line 12 calls a string method: customer.toUpperCase() gives ASHA, so the heading reads Thanks, ASHA!.
  • Line 13 reads a property: items.length is 3, so the paragraph reads You ordered 3 items.
  • Line 14 reads the first item of the array: items[0] is Green tea.
  • Line 15 calls formatPrice, the function on lines 5 to 7, which turns 11.5 into $11.50 with toFixed(2). Moving a long expression into a function keeps the markup easy to read.

Why does any expression work? Remember from Writing markup with JSX that every tag becomes a call to jsx(), with what's between the tags in children. Here is line 13 as TypeScript's compiler writes it:

Line 13, compiledJavaScript
jsxs("p", { children: ["You ordered ", items.length, " items."] });

The text and the braces became one array of children: the text "You ordered ", then the expression items.length exactly as you wrote it, then " items.". The braces simply drop your JavaScript into the call. So anything you could write as a value in an array can go in the braces, and React shows the value it gets.

But React can't show every value. Pick one below, and React renders it on this page between square brackets, which are plain text so you can see where the value goes:

Playground: what React shows for a value
The value on line 1, the braces on line 4JSX
const value = "Green tea"; export default function Shelf() {  return <p>[{value}]</p>;}
The pagerendering
Try it yourself

Rendering…

React renders the paragraph on this page for real, each time you pick a value. The square brackets are plain text around the braces, so you can see where the value goes, even when it adds nothing.

Strings and numbers show. Even 0 and NaN show, because they're numbers. true, false, null, and undefined leave nothing, so the brackets stay empty: React skips them on purpose, which is what lets you write conditions in braces later. An array shows each item, one after another. A plain object stops React with an error.

Four rows. Text and numbers, like "Green tea", 3 and 0: shown. true, false, null and undefined: nothing shown. An array: each item shown. A plain object: an error.between the braceson the page"Green tea", 3, 0shows ittrue, false, null, undefinedshows nothing["Tea", "Cake"]shows each item{ name: "Tea" }an error
Text and numbers show, even 0. Booleans, null, and undefined leave nothing behind. An array shows each item. A plain object stops React with an error.

Here is the object case in a real component, with its error on purpose:

A whole object between tags (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
const tea = { name: "Green tea", price: 3 }; export default function TeaCard() {  return <h3>{tea}</h3>;}
ResultLoading…

Line 1 makes an object with two properties, and line 4 puts the whole object between the tags. The result shows React's error: Objects are not valid as a React child (found: object with keys {name, price}). A child is anything between a tag's opening and closing parts. React has no way to know whether you wanted the name, the price, or both. Press Edit & run and change {tea} to {tea.name}: the heading shows Green tea.

How to read this table: each row is one kind of value between the braces, what the page shows, and why.

What each value shows
When the braces holdThe page showsWhy
Text, like "Green tea"The text.A string is what a page is made of.
A number, like 3, 0, or NaNThe number, even 0 and NaN.Numbers are values worth showing, so React never skips them.
true or falseNothing.React skips booleans, so a condition in braces can leave no trace.
null or undefinedNothing.Both mean "no value", so there is nothing to show.
An array, like ["Tea", "Cake"]Each item, one after another.React shows every item in order. Lists are built this way.
A plain object, like { name: "Tea" }An error, and nothing renders.React can't guess which property you meant. Write one, like tea.name.

Only an expression fits in the braces

JavaScript code comes in two kinds, as the Code structure lesson explains. An expression works out to a value: price * cups, tea.name, formatPrice(total). A statement does something but gives back no value: if, for, const total = 3. The braces need a value, so only an expression fits. Here is an if in the braces:

An if statement inside braces (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
const cupsLeft = 3; export default function StockBadge() {  return (    <p>      {if (cupsLeft > 0) { "In stock" } else { "Sold out" }}    </p>  );}
ResultLoading…

Nothing renders. The result shows a syntax error, Unexpected token (6:8): line 6, at the if. Picture what the compiler would have to write, with the braces dropped into the call as they are:

What line 6 would compile to (not valid JavaScript)Text
jsx("p", {
  children: if (cupsLeft > 0) { "In stock" } else { "Sold out" },
});

Line 2 puts an if where a property's value goes. That's not JavaScript, any more than const label = if (…) is. There are two ways to fix it. Inside the braces, use an expression that makes the choice, the ternary operator condition ? a : b. Or make the choice with if before the return, store the result in a variable, and show the variable:

Two fixes: a ternary in the braces, an if before the returnPop out in the code editor (opens in a new tab)JSX
const cupsLeft = 3; export default function StockBadge() {  let note = "";  if (cupsLeft > 0 && cupsLeft < 5) {    note = " Order soon!";  }   return (    <p>      {cupsLeft > 0 ? `${cupsLeft} cups left.` : "Sold out."}      {note}    </p>  );}
ResultLoading…

Line 11 is a ternary inside the braces: when cupsLeft > 0, it gives the template literal `${cupsLeft} cups left.`, and otherwise "Sold out.". Lines 4 to 7 are an ordinary if, above the return, where statements are fine: it sets note when only a few cups are left. Line 12 shows note. The result reads 3 cups left. Order soon!

Press Edit & run and set cupsLeft on line 1 to 0: Sold out., and the if leaves note empty. Set it to 20: 20 cups left., with no note. Loops work the same way: a for loop can't go in braces, but items.map(…) is an expression that gives back an array, which is how the Rendering lists lesson builds lists. Choosing between whole pieces of markup gets its own lesson too, Conditional rendering.

Braces as an attribute's value

Braces work in a second place: right after the = of an attribute, in place of the quotes. The attribute then gets the expression's value. This sign-up form takes four attributes from variables:

A sign-up form with attributes from variablesPop out in the code editor (opens in a new tab)JSX
const field = "email";const hint = "you@example.com";const maxLetters = 40;const joined = false; export default function JoinForm() {  return (    <form className={joined ? "join done" : "join"}>      <label htmlFor={field}>Email</label>      <input id={field} type="email" placeholder={hint} maxLength={maxLetters} />      <button type="button" disabled={joined}>        {joined ? "You're in" : "Join"}      </button>    </form>  );}
ResultLoading…
  • Line 8: className gets a ternary. While joined is false, the form's class is join.
  • Lines 9 and 10: htmlFor and id both read field, so the label and the box always match. Click Email in the result: the cursor jumps into the box.
  • Line 10: placeholder gets the string in hint, the grey text in the empty box, and maxLength gets the number 40, so the box takes at most 40 letters.
  • Line 11: disabled gets a boolean. false leaves the attribute off, so the button works.

Press Edit & run and set joined on line 4 to true. Three things change from one value: the class becomes join done, the button is disabled, and line 12 makes it say You're in.

Quotes and braces are easy to mix up. Here is the same name, hint, written three ways:

Quotes, quotes around braces, and bracesPop out in the code editor (opens in a new tab)JSX
const hint = "you@example.com"; export default function ThreeBoxes() {  return (    <>      <input placeholder="hint" />      <input placeholder="{hint}" />      <input placeholder={hint} />    </>  );}
ResultLoading…

The first box, line 6, says hint: quotes give the attribute exactly the text inside them. The second, line 7, says {hint}, braces and all: inside quotes, braces are ordinary characters. Only the third, line 8, shows you@example.com, the variable's value. The middle one is a common slip, and nothing warns you about it.

Two rows. placeholder="hint" in quotes gives the box the text hint. placeholder={hint} in braces gives the box the value of the variable hint, you@example.com.you writethe box showsplaceholder="hint"hintthe text in the quotesplaceholder={hint}you@example.comthe value of the variable hint
In quotes, the attribute gets exactly the text you typed. In braces, it gets the value of the JavaScript inside, here the variable hint.

Use quotes for fixed text and braces for everything else. Braces matter most for values that aren't text. Here is false written both ways on a button:

false in braces, and in quotesPop out in the code editor (opens in a new tab)JSX
export default function OrderButtons() {  return (    <>      <button disabled={false}>Order with braces</button>      <button disabled="false">Order with quotes</button>    </>  );}
ResultLoading…

Line 4 gives disabled the boolean false, so the first button works. Line 5 gives it the string "false", and a non-empty string counts as true, so the second button is disabled. Try clicking both. Press Edit & run to read React's warning in the console: Received the string `false` for the boolean attribute `disabled`. … Did you mean disabled={false}?

Braces go only in those two places: between tags, and right after an attribute's =. They can't make a tag name or an attribute name. This doesn't compile, because the compiler needs to read every tag's name as it is:

Braces can't name a tag (does not compile)JSX
const tag = "h3";

<{tag}>Green tea</{tag}>

How to read this table: each row is one way to write an attribute's value, what the attribute gets, and when to use it.

Quotes or braces
When you writeThe attribute getsUse it for
placeholder="hint"The text hint.Fixed text that never changes.
placeholder="{hint}"The text {hint}, braces and all.Nothing. It's almost always a mistake.
placeholder={hint}The value of the variable hint.Text that comes from your data.
A template literal in bracesText built from fixed words and values.Mixing words with data, like Search 12 teas.
maxLength={40}The number 40.Numbers, so you can do sums with them first.
disabled={joined}The attribute when joined is true, none when false.On and off attributes, from a condition.

Double curlies: a style object

In HTML, style takes a string of CSS. In JSX it takes an object, with one property per CSS declaration. An object literal has braces of its own, so when you write it right in the attribute, you get two pairs:

A sale badge with a style objectPop out in the code editor (opens in a new tab)JSX
export default function SaleBadge() {  return (    <p      style={{        display: "inline-block",        backgroundColor: "#c2410c",        color: "white",        padding: 6,        borderRadius: 4,      }}    >      20% off green teas    </p>  );}
ResultLoading…

Line 4 opens two braces. The outer one switches to JavaScript, as for any attribute. The inner one starts an object, which closes on line 10 with }}. Lines 5 to 9 are its properties. They're named in camelCase, like the element.style properties in plain JavaScript: backgroundColor on line 6 for CSS's background-color, borderRadius on line 9 for border-radius. Line 8 gives padding a plain number, 6, and React writes 6px.

Nothing about double curlies is special. Put the object in a variable, and you're back to single braces. This badge looks exactly the same:

The same style object in a variablePop out in the code editor (opens in a new tab)JSX
const badgeStyle = {  display: "inline-block",  backgroundColor: "#c2410c",  color: "white",  padding: 6,  borderRadius: 4,}; export default function SaleBadge() {  return <p style={badgeStyle}>20% off green teas</p>;}
ResultLoading…

Lines 1 to 7 make the object, and line 10 passes it with one pair of braces: style={badgeStyle}. Both badges get the same style attribute on the page.

Three cards with arrows between them. style= with outer braces, labelled JavaScript here. Then the object padding 6, labelled the inner braces make an object. Then padding: 6px, labelled the CSS React writes.style={ … }outer braces:JavaScript here{ padding: 6 }inner braces:an objectpadding: 6pxthe CSS thatReact writes
style={{ padding: 6 }} is one pair of braces inside another. The outer pair switches to JavaScript, the inner pair is an object, and React turns each of its properties into CSS on the element.

A string of CSS, the HTML way, is an error on purpose here:

A CSS string for style (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
export default function SaleBadge() {  return <p style="color: #c2410c; padding: 6px">20% off green teas</p>;}
ResultLoading…

React stops at line 2 and says so: The `style` prop expects a mapping from style properties to values, not a string. A mapping here means an object. Turn the string into {{ color: "#c2410c", padding: 6 }} with Edit & run, and the badge text appears.

Which numbers get px? Pick a style object below, and see the style attribute React really writes:

Playground: the CSS a style object becomes
A paragraph with a style objectJSX
export default function TeaName() {  const style = { fontSize: 20 };  return <p style={style}>Green tea</p>;}
The pagestyle attribute

Green tea

…

Try it yourself

Rendering…

React renders the paragraph on this page with your style object, and the style attribute is read back from the element afterwards.

Sizes like fontSize and padding get px. Properties that have no unit in CSS, like lineHeight, opacity, and zIndex, keep the bare number. For any other unit, write a string: "1.25rem".

Most styling belongs in a CSS file, with className; the Styling components lesson compares the ways to style. A style object earns its place when a value comes from your data, like how full a loyalty card is:

A loyalty card whose bar width comes from dataPop out in the code editor (opens in a new tab)JSX
const stamps = 7;const needed = 10; export default function LoyaltyCard() {  const percent = (stamps / needed) * 100;   return (    <section>      <p>{stamps} of {needed} stamps. Your next tea is free at {needed}.</p>      <div className="bar">        <div className="fill" style={{ width: `${percent}%` }} />      </div>    </section>  );}
ResultLoading…

Line 5 works out the percent: 7 of 10 is 70. Line 11 sets only the width in a style object, with a template literal that makes "70%". The bar's fixed look, its height, colors, and rounded ends, sits in the styles.css tab under the class names on lines 10 and 11. Change stamps on line 1 to 9 and the bar grows to 90%.

A product card built from one object

In a real shop, product cards come from data, often one object per product from a server. Every part of this card that can change reads the product object, using each kind of braces from this lesson:

A product card built from one objectPop out in the code editor (opens in a new tab)JSX
const product = {  name: "Jasmine green tea",  price: 4.5,  stock: 3,  color: "#15803d",  tags: ["green", "floral"],}; export default function ProductCard() {  const lowStock = product.stock < 5;   return (    <article className="product" style={{ borderLeftColor: product.color }}>      <h3>{product.name}</h3>      <p>${product.price.toFixed(2)} · {product.tags.join(", ")}</p>      <p className={lowStock ? "stock low" : "stock"}>        {lowStock ? `Only ${product.stock} left` : "In stock"}      </p>      <button aria-label={`Add ${product.name} to cart`} disabled={product.stock === 0}>        Add to cart      </button>    </article>  );}
ResultLoading…
  • Line 10 works out a value before the return: lowStock is true when fewer than 5 are left.
  • Line 13 gets the border color from the data with a style object, borderLeftColor. The rest of the card's look is in the styles.css tab.
  • Line 15 shows the price with two decimals, $4.50, and joins the tags into green, floral.
  • Lines 16 and 17 pick a class and a message with two ternaries: with 3 left, the line says Only 3 left, in the low style.
  • Line 19 builds the button's aria-label, what a screen reader says, from the name, and turns the button off at 0.

Press Edit & run and set stock on line 4 to 12: the line says In stock. Set it to 0: the button turns off, but the line says Only 0 left. Exercise 4 fixes that.

Fits in the braces?
  • price * cups
  • if (stock > 0) "In stock"
  • tea.name.toUpperCase()
  • for (const tea of teas) tea
  • stock > 0 ? "In stock" : "Sold out"
  • const total = price * cups
  • `Only ${stock} left`
  • formatPrice(total)
Try it yourself
0 of 8 correct

Sort each piece of code: could it go between the braces in <p>{…}</p>?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "${price} in JSX is a template literal." No. Outside backticks, the $ is plain text and {price} is a pair of braces. That's why ${price} shows $3.
  • "Anything can go in the braces." No. Only an expression, code that makes a value. if, for, and const are statements: put them above the return.
  • "Quotes and braces do the same thing in an attribute." No. placeholder="{hint}" shows the text {hint}. Only placeholder={hint} reads the variable.
  • "React shows false and null as words." No. It shows nothing for true, false, null, and undefined. But it does show 0 and NaN.
  • "Double curlies are special JSX syntax." No. They're an object literal inside ordinary braces.
  • "style takes CSS text, like in HTML." No. It takes an object with camelCase names; a string throws an error.
  • "disabled="false" turns the attribute off." No. The string "false" turns it on. Write disabled={false}.

Practice exercises

Each exercise shows its result under the code, or when you press Edit & run. Change the code, and the result updates as you type. Try it yourself first, then use the hints.

Exercise 1 · Warm-upPredict the price line

What does the paragraph show? Answer first, then press Edit & run to check.

const tea = "Iced tea";
const price = 4;
const cups = 3;

export default function TeaCard() {
  return (
    <article>
      <h3>{tea}</h3>
      <p>{cups} cups cost ${price * cups}</p>
    </article>
  );
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upShow an object's parts

    Press Edit & run: the heading throws an error. Make it show the tea's name and price, like Oolong: $5.

    const tea = { name: "Oolong", price: 5 };
    
    export default function TeaTitle() {
      return <h3>{tea}</h3>;
    }

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticePut the count in the search box

      The box says Search {count} teas. Make it show the real number from count.

      const count = 12; export default function SearchBox() {  return <input type="search" placeholder="Search {count} teas" />;}
      ResultLoading…

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeSay Sold out at zero

        With stock: 0, the card says Only 0 left. Make it say Sold out when nothing is left, and keep the other two messages.

        const product = {  name: "Jasmine green tea",  price: 4.5,  stock: 0,  color: "#15803d",  tags: ["green", "floral"],}; export default function ProductCard() {  const lowStock = product.stock < 5;   return (    <article className="product" style={{ borderLeftColor: product.color }}>      <h3>{product.name}</h3>      <p>${product.price.toFixed(2)} · {product.tags.join(", ")}</p>      <p className={lowStock ? "stock low" : "stock"}>        {lowStock ? `Only ${product.stock} left` : "In stock"}      </p>      <button aria-label={`Add ${product.name} to cart`} disabled={product.stock === 0}>        Add to cart      </button>    </article>  );}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeFix the loyalty bar

          The card has 7 of 10 stamps, but the bar's fill is short. Make the fill cover 70% of the bar.

          const stamps = 7;const needed = 10; export default function LoyaltyCard() {  const percent = (stamps / needed) * 100;   return (    <section>      <p>{stamps} of {needed} stamps. Your next tea is free at {needed}.</p>      <div className="bar">        <div className="fill" style={{ width: percent }} />      </div>    </section>  );}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeWrite a receipt line

            The line says 1 items · $4.5. Make it say item for one and items for more, with the price in two decimals.

            const count = 1;const total = 4.5; export default function ReceiptLine() {  return (    <p>      {count} items · ${total}    </p>  );}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each code question, find what's inside each pair of braces first, then work out its value. Press Edit & run on a question's code to check your answer.

              The curly braces quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What text does this order line show?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                const cups = 0;
                const sugar = false;
                
                export default function Order() {
                  return <p>Cups: {cups}, sugar: {sugar}</p>;
                }

                Choose an answer to see the explanation.

              2. Question 2 of 7Which of these can go inside JSX curly braces?

                Choose an answer to see the explanation.

              3. Question 3 of 7What does this box show as its placeholder?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                const city = "Pune";
                
                export default function CityBox() {
                  return <input placeholder="{city}" />;
                }

                Choose an answer to see the explanation.

              4. Question 4 of 7Why does style={{ color: "teal" }} have two pairs of braces?

                Choose an answer to see the explanation.

              5. Question 5 of 7What does React write in this paragraph's style attribute?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                export default function Note() {
                  return <p style={{ padding: 8, lineHeight: 1.5 }}>Steep for 3 minutes.</p>;
                }

                Choose an answer to see the explanation.

              6. Question 6 of 7What happens with <button disabled="false">Order</button>?

                Choose an answer to see the explanation.

              7. Question 7 of 7What happens when this order line renders?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                const order = { tea: "Chai", cups: 2 };
                
                export default function OrderLine() {
                  return <p>{order}</p>;
                }

                Choose an answer to see the explanation.

              Key takeaways

              • Curly braces in JSX switch to JavaScript. React works out what's inside each time the component runs.
              • Braces go in two places: between tags, to show a value, and right after an attribute's =, to set it.
              • Only an expression fits, because the braces become a value in a jsx() call. Use a ternary in the braces, or an if before the return.
              • Strings and numbers show, even 0. true, false, null, and undefined show nothing. Arrays show each item. Plain objects are an error.
              • Quotes give an attribute fixed text; braces give it a value. style takes an object, so writing it in place gives double curlies, with camelCase names and px added to plain numbers on sizes.

              Remember the one-liner.
              Braces are a window into JavaScript: one expression in, one value out, on the page.

              Coming next: Passing props to a component. It uses these braces to give each component its own data, the way a menu page gives each tea card its own name and price.

              CompleteFrontend Clear concepts. Working examples.