Topic 1.3
JavaScript in JSX with Curly Braces
In one line
Curly braces { } open a window from JSX back into JavaScript: inside them you can put any expression (a variable, a calculation, a function call, a ternary, a template literal), as text or as an attribute value, but not a statement such as if or for.
Think of it like this
A greeting card with blanks: "Happy birthday, ____! You are ____ today." The card is printed once, and you fill the blanks for each friend. In JSX the tags are the printed card and curly braces are the blanks: <h1>Happy birthday, {name}!</h1>. Whatever JavaScript value you put in the blank is shown there.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Curly braces
{ }in JSX: a spot where you write JavaScript instead of fixed text.- Expression
- Code that produces a value:
count,a + b,fn(),ok ? "yes" : "no". - Statement
- Code that does something without producing a value:
if,for,let x = 1. Not allowed in braces. - Ternary
- The expression form of if/else:
condition ? valueIfTrue : valueIfFalse. - Object literal
- An object written directly in code:
{ color: "red" }. The inner pair in{{ }}. - Template literal
- A string in backticks that can include values: `
Hi ${name}`. - Escaping
- Turning special characters like
<into safe text so they are shown, not run. - XSS
- Cross-site scripting: an attack where someone's text gets run as code in your page. JSX braces prevent it.
Step by step
01Fixed text first
Start with a card where everything is typed in by hand. It works for one user only.
<div className="profile">
<h2>Asha Rao</h2>
<p>1,250 pts</p>
</div>02Open blanks with braces
Replace the fixed parts with expressions. The name is calculated, the points come from a function call. The output is the same, but now it follows the data.
const fullName = user.first + " " + user.last;
<div className="profile">
<h2>{fullName}</h2>
<p>{formatPoints(user.points)}</p>
</div>03Braces in attributes
Attributes take braces too. A number like width={48} stays a number. A template literal builds a string from pieces. No quotes around the braces.
<img src={user.avatar} alt={`Photo of ${fullName}`} width={48} height={48} />04Statements before the return, expressions inside
Need an if with several branches? Do it above the return and store the result in a variable. Inside the JSX, use the variable, or a ternary for a quick two-way choice.
05Text is always text
If a value is a string containing <b>, React shows the characters. You'll see that in the second live example: the page shows the tags, and the HTML contains <b>.
Try it yourself
- 1
Change the data, not the JSX
In the profile example, change
pointsto400. Predict the three log lines before running. (400 pts · level 1,status plain, and the alt text is unchanged.) - 2
Choose the tag at run time
In the forecast example, add
const Heading = weather.temp > 30 ? "h1" : "h2";above thereturnand use<Heading>…</Heading>instead of<h2>. The heading gets bigger on hot days. - 3
Try to inject HTML
Change
commentto"<img src=x onerror=alert(1)>". Predict: does anything pop up? No. React shows the characters as text, so the string can't run. This is why braces are safe.
Code & diagrams
Every blank is an expression: a variable (`fullName`), a function call (`formatPoints`), arithmetic (`Math.floor(...) + 1`), template literals in attributes and a ternary for the label. Change `points` to `400` and watch the level, the label and the class follow.
Preview
Console
Nothing logged yet.Expected console output
Photo of Asha Rao
1,250 pts · level 3
status goldThe `if` lives above the `return`; inside the JSX there is only a variable and a ternary. The style uses double braces: JavaScript braces around an object. The comment's `<b>` is shown as characters: React escaped it to `<b>`.
Preview
Console
Nothing logged yet.Expected console output
Visitor says: <b>Nice</b> day!const url = "/logo.png";
const Tag = "h2"; // a tag chosen at run time: capitalised variable
<img src={url} /> // OK: attribute value
<p>{1 + 2}</p> // OK: child (shows 3)
<Tag>Title</Tag> // OK: renders <h2>
<Card {...props} /> // OK: spread all fields as attributes
<img src="{url}" /> // wrong: the literal text {url}
<{Tag}>Title</{Tag}> // syntax error: braces can't be a tag name
<p>{if (ok) { "yes" }}</p> // syntax error: if is a statement
<p>{ok ? "yes" : "no"}</p> // OK: the expression formBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Put an if inside the braces
In the forecast example, replace <p>{advice}</p> with <p>{if (weather.temp > 30) { "Drink water" }}</p>.
Break #2
Quote the braces
In the profile example, change src={user.avatar} to src="{user.avatar}".
Myth vs fact
Myth
Double braces {{ }} are special JSX syntax.
Fact
They're two ordinary things: the JSX braces and an object literal inside them. style={{ color: "red" }} is the same as const s = { color: "red" }; style={s}.
Myth
You can't use if in React.
Fact
You can use any JavaScript in the component body. Only the inside of JSX braces is limited to expressions. Compute with if above the return.
Myth
Putting user text into JSX is dangerous.
Fact
JSX escapes strings, so user text is shown as text. The risks are dangerouslySetInnerHTML, javascript: URLs in href (React 19 blocks them) and other raw-HTML paths, covered in Phase 21.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Because braces hold expressions, everything inside runs on every render. Keep it cheap: formatting a number is fine; filtering 10,000 items on each keystroke may not be (Phase 16 covers
useMemoand the React Compiler). - ▸
Avoid IIFEs (
{(() => { if (...) return a; return b; })()}) inside JSX. They work, but extracting a variable or a small component is clearer and lets tools such as the React Compiler optimise it. - ▸
Escaping happens in
react-domwhen it creates text nodes and sets attributes: text goes in through text-node APIs, never through HTML parsing. On the server,renderToStringescapes<,>,&,"and'in text and attributes for the same reason.
Remember this
- 1
Inside JSX, anything between
{and}is plain JavaScript, worked out every time the component renders. It must be an expression: code that produces a value.name,2 + 3,user.first + " " + user.last,items.length,formatPrice(99),isOpen ? "Open" : "Closed"are all expressions. If you could write it on the right side ofconst x = …, it fits in braces. - 2
A statement is code that *does* something but doesn't produce a value:
if,for,while,let x = 1,switch. Statements can't go inside braces, because the braces become an argument of a function call, and you can't pass anifas an argument. The fix is simple: do statement work **before thereturn** (compute variables withif), or use the expression versions inside JSX: the ternarya ? b : cinstead ofif/else,&&for "show only if" (Topic 1.4) andmapinstead offor(Phase 3). - 3
Braces can go in exactly two places: as children (text content between tags,
<p>{count}</p>) and as an attribute value (<img src={url} />). For an attribute, write either quotes for a fixed string (alt="Logo") or braces for a JavaScript value (alt={title}), never both:src="{url}"is the literal text{url}. Braces can't replace a tag name: to choose a tag at run time, store it in a capitalised variable (const Tag = level === 1 ? "h1" : "h2"; <Tag>…</Tag>). A third form, spread<Card {...props} />, passes every field of an object as an attribute (Phase 2). - 4
Double braces
{{ }}are not special syntax. Instyle={{ color: "red" }}the outer pair means "JavaScript here" and the inner pair is an ordinary object literal. The same happens whenever you pass an object inline:<Chart options={{ animate: false }} />. - 5
Template literals (strings in backticks, `
Hello, ${name}) are expressions too, handy for attributes built from pieces:alt={Photo of ${name}}orclassName={card ${active ? "active" : ""}}. Inside children you rarely need them, becauseHello, {name}` already mixes text and values. - 6
Braces are safe by default. When you put a string in braces, React inserts it as text, escaping characters like
<and&. A string such as"<b>Nice</b>"appears with its angle brackets visible; it never becomes HTML. This protects you from cross-site scripting (XSS: an attacker's text being run as code in your page). The only way to insert raw HTML is the deliberately scary propdangerouslySetInnerHTML, covered with sanitising in Phase 21.
Explain it without notes
What can you put inside curly braces in JSX, and what can't you?
Where in JSX can curly braces appear?
Why does style={{ color: "red" }} have two pairs of braces?
Why is rendering a user's comment with {comment} safe?
Practice
Given const item = { name: "Pen", price: 20, qty: 3 }, render Pen × 3 = ₹60 using braces and a calculation.
Render a <p> whose class is "open" when const isOpen = true and "closed" otherwise, and whose text says We are open or Sorry, closed.
Trade-offs
- ↔
Long expressions inside JSX are hard to read. Move calculations into named variables above the
return: the JSX then reads like the page it produces. - ↔
Ternaries are great for two choices; chained ternaries for three or more get confusing. Use an
if/switchabove the return, or a lookup object (const icons = { sunny: "…", rainy: "…" }).
Done when you can
I can put variables, calculations and function calls in JSX with braces.
I can tell an expression from a statement.
I use braces or quotes for an attribute, never both.
I can explain double braces in
style={{ }}.I can build attribute strings with template literals.
I know JSX escapes text and why that matters.