← Back to React
Lesson 6 · React Fundamentals

JSX Expressions

Embedding values with braces, and what JSX refuses to render.

Beginner35 min

What you will be able to do

  • Use {} to put any JavaScript expression into content or an attribute
  • Tell an expression from a statement, and know why if cannot go in braces
  • Predict what React shows for strings, numbers, booleans, null, and undefined
  • Explain why {count && <Badge />} can print a stray 0 - and fix it
  • Choose between &&, a ternary, and an early return
  • Render object properties and arrays, and explain why a plain object fails
  • Build dynamic attributes - src, disabled, and className

The idea, in plain English

Curly braces in JSX switch from markup back to JavaScript. Whatever is inside is evaluated, and the value it produces is placed into the element - as text, as an attribute, or as more elements.

Only expressions fit, because the braces become an argument to a function call, and a function argument must be a value. name, price * quantity, user.name, isLoggedIn ? "Logout" : "Login", and items.map(...) all produce values. if, for, and const do not, so they cannot go inside braces.

React then decides what each value looks like. Strings and numbers appear as text - including 0. true, false, null, undefined, and the empty string appear as nothing at all, which is exactly what makes conditions like {isAdmin && <AdminPanel />} work. A plain object cannot be shown and throws an error.

Nearly every bug in this lesson comes from the gap between those two rules: 0 is falsy in JavaScript, but React still prints it.

Worked example: Why false, null, and undefined print nothing but 0 prints.

Braces switch to JavaScript

In <h1>Hello name</h1>, name is just four letters of text. In <h1>Hello {name}</h1>, name is a variable: React evaluates it and shows its value.

Braces can hold any expression - a variable, a calculation such as {price * quantity}, a property such as {user.name}, a method call such as {name.toUpperCase()}, or a function call such as {formatPrice(price)}. Each one is evaluated every time the component renders.

Expressions, not statements

An expression produces a value: 10 + 20, user.name, items.length, isLoggedIn ? "Welcome" : "Please login". A statement performs an action and produces nothing you could pass along: if, for, while, switch, const.

JSX compiles braces into a function argument, so {if (isLoggedIn) { ... }} is a syntax error - the compiler reports "Expression expected." Use an expression form instead: a ternary for either-or, && for show-or-nothing, or move the if above the return.

What React shows for each value

Strings and numbers are rendered as text. That includes 0, and it includes NaN, which React prints as "NaN" and warns about in development.

true, false, null, undefined, and the empty string render nothing. React ignores booleans on purpose, so that conditions can live directly inside JSX.

Arrays are rendered item by item, so {["React", "Node"]} shows ReactNode. A plain object is not a valid child: React throws "Objects are not valid as a React child (found: object with keys {name, age})", and TypeScript rejects it before you run it.

What appears on screen
{"React"}React
{123}123
{0}0 - a number, so it is shown
{NaN}NaN, with a development warning
{""}nothing
{true} {false}nothing
{null} {undefined}nothing
{["React", "Node"]}ReactNode - each item in turn
{<h1>Hi</h1>}the element
{{ name: "Chandu" }}Error: Objects are not valid as a React child

Conditions with &&

a && b returns a if a is falsy, and b otherwise. So {isAdmin && <button>Admin Settings</button>} returns the button when isAdmin is true, and false when it is not - and false renders nothing.

This is the most common way to show something or nothing. It works well whenever the left side is a real boolean.

The 0 && trap

When the left side is a number, && goes wrong. With messages = 0, the expression {messages && <p>You have messages</p>} returns messages itself - the number 0 - because 0 is falsy. React shows numbers, so a stray 0 appears on the page.

Make the left side a real boolean. {messages > 0 && <p>...</p>} returns false for 0, and false renders nothing. Boolean(messages) and !!messages work too, but messages > 0 says what you mean when you are checking a count.

An empty string on the left is harmless, because React renders "" as nothing. The trap is specific to 0 - and to NaN, which is also falsy and also printed.

Watch out: Any time the left side of && is a number - a count, a length, an index - compare it explicitly. {items.length && <List />} prints 0 for an empty list.

Either-or with a ternary

condition ? a : b picks one of two values, and both can be JSX: {isLoggedIn ? <button>Logout</button> : <button>Login</button>}. Use it when there are two outcomes; use && when the other outcome is nothing.

Ternaries can be chained for three or more states, but they get hard to read quickly. When they do, compute the result above the return, split the piece into its own component, or return early.

Early returns

A component is a function, so it can return before it reaches the main JSX. if (!user) return <p>Loading...</p>; handles the special case first, and the rest of the function can assume user exists.

This is where if belongs - above the JSX, not inside the braces. It usually reads better than a ternary wrapped around the whole component.

Functions in JSX, and keeping render pure

Calling a function inside braces is normal: {formatPrice(price)}, {formatName(first, last)}. Formatting belongs in small functions, not in long expressions inside the markup.

Those functions should only calculate. React may render a component more than once - StrictMode does it on purpose in development - so a function that saves to a database or sends a request during render would do that more than once too. Side effects belong in event handlers or useEffect, later in the course.

Expressions in attributes

Braces work in attributes too: src={imageUrl}, alt={title}, disabled={isLoading}. React sets a boolean attribute when the value is true and leaves it off entirely when it is false - disabled={false} produces a button with no disabled attribute.

className often depends on state. For two options, a ternary is clearest: className={isActive ? "card active" : "card"}. A template literal such as className={`card ${isActive ? "active" : ""}`} also works, but leaves a trailing space when the condition is false.

Syntax and examples

Variables, calculations, and calls
function Order() { const name = "Chandu"; const price = 100; const quantity = 3; return ( <div> <h1>Hello {name}</h1> <p>Total: {price * quantity}</p> <p>{name.toUpperCase()}</p> <p>Items: {[10, 20, 30].length}</p> </div> ); } // Hello Chandu // Total: 300 // CHANDU // Items: 3
Every value, rendered - checked in React 19
<p>{"React"}</p>; // <p>React</p> <p>{123}</p>; // <p>123</p> <p>{0}</p>; // <p>0</p> <p>{true}</p>; // <p></p> <p>{false}</p>; // <p></p> <p>{null}</p>; // <p></p> <p>{undefined}</p>; // <p></p> <p>{""}</p>; // <p></p> <p>{["React", "Node"]}</p>; // <p>ReactNode</p> <div>{{ name: "Chandu", age: 34 }}</div>; // Error: Objects are not valid as a React child // (found: object with keys {name, age})
The 0 && trap, and three fixes
const messages = 0; // Renders <div>0</div> - 0 && anything is 0, and React prints numbers <div>{messages && <p>You have messages</p>}</div>; // Renders <div></div> - the left side is now a real boolean <div>{messages > 0 && <p>You have messages</p>}</div>; <div>{Boolean(messages) && <p>You have messages</p>}</div>; <div>{!!messages && <p>You have messages</p>}</div>
A statement will not compile
// Error: Expression expected. <div> {if (isLoggedIn) { "Welcome" }} </div> // An expression works <div> {isLoggedIn ? "Welcome" : "Please login"} </div>
&&, ternary, and early return
type User = { name: string; isAdmin: boolean }; function Dashboard({ user }: { user?: User }) { // Early return: handle the special case before the main JSX if (!user) { return <p>Loading...</p>; } return ( <div> <h1>Hello {user.name}</h1> {/* Something or nothing */} {user.isAdmin && <button>Admin Settings</button>} {/* One of two */} {user.isAdmin ? <p>Full access</p> : <p>Read-only access</p>} </div> ); }
Three states without nested ternaries
type Status = "loading" | "error" | "ready"; function StatusMessage({ status }: { status: Status }) { // Hard to read once it grows: // {status === "loading" ? <p>Loading...</p> : status === "error" ? <p>Error</p> : <p>Loaded</p>} let message; if (status === "loading") message = <p>Loading...</p>; else if (status === "error") message = <p>Error occurred</p>; else message = <p>Data loaded</p>; return <div>{message}</div>; }
Formatting with functions
function formatPrice(price: number) { return "₹" + price.toLocaleString("en-IN"); } function formatName(firstName: string, lastName: string) { return `${firstName} ${lastName}`; } function Product() { return ( <div> <h2>{formatName("Chandu", "Nandigam")}</h2> <p>Price: {formatPrice(125000)}</p> </div> ); } // Chandu Nandigam // Price: ₹1,25,000
Objects: properties yes, the object no
const user = { name: "Chandu", age: 34 }; <div>{user.name}</div>; // Chandu <div>{user.age}</div>; // 34 <div>{JSON.stringify(user)}</div>; // {"name":"Chandu","age":34} - for debugging <div>{user}</div>; // Error - and a TypeScript error first
Arrays, with map
const courses = [ { id: 1, title: "React" }, { id: 2, title: "Node.js" }, { id: 3, title: "Python" }, ]; function CourseList() { return ( <ul> {courses.map((course) => ( <li key={course.id}>{course.title}</li> ))} </ul> ); }
Dynamic attributes
const imageUrl = "/images/react.png"; const isLoading = true; const isActive = false; const userId = 101; <img src={imageUrl} alt="React" />; <button disabled={isLoading}>Submit</button>; // disabled="" (false: no attribute at all) <div className={isActive ? "card active" : "card"}>Course</div>; <div className={`user-${userId}`}>User</div>; // class="user-101"
Numbers that need formatting
const completed = 1; const total = 3; <p>{(completed / total) * 100}%</p>; // 33.33333333333333% <p>{Math.round((completed / total) * 100)}%</p>; // 33%

Tip: Ask one question of any value inside braces: will React show it? Strings and numbers yes, booleans, null, undefined, and "" no, plain objects - error.

Watch out: TypeScript does not catch the 0 && trap - a number is a perfectly valid child. You have to spot it yourself, which is why it is such a common interview question.

Expressions you will write every day

Each of these produces a value, so each can go inside braces.

Variable

Show a value.

{name}
Property

Show part of an object.

{user.name}
Calculation

Compute while rendering.

{price * quantity}
Function call

Format a value.

{formatPrice(price)}
&&

Something or nothing - keep the left side boolean.

{count > 0 && <Badge />}
Ternary

One of two.

{isLoggedIn ? <Logout /> : <Login />}
map

One element per item, each with a key.

{items.map((i) => <Item key={i.id} />)}
Attribute

Any attribute value.

<button disabled={isLoading}>

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Print the table

“Render <p>{value}</p> for "React", 0, false, null, undefined, and "". Which paragraphs are empty in the Elements panel?”

Catch the stray 0

“Render {count && <p>New messages</p>} with count = 5, then with count = 0. Fix it so 0 shows nothing.”

Render an object

“Put {user} directly into a div. Read the TypeScript error, then the runtime error, and fix it with user.name.”

Toggle an attribute

“Render <button disabled={isLoading}> with isLoading true and then false, and inspect the button each time. Does the attribute disappear or become "false"?”

What usually goes wrong

Forgetting the braces

Without braces, a variable name is only text.

✗ <h1>name</h1>
✓ <h1>{name}</h1>
Putting if inside braces

if is a statement, so the compiler reports "Expression expected." Use &&, a ternary, or an if above the return.

✗ {if (isLoggedIn) { return <Dashboard />; }}
✓ {isLoggedIn ? <Dashboard /> : <Login />}
Using a number on the left of &&

When the number is 0, the expression returns 0 and React prints it.

✗ {count && <Badge />}
✓ {count > 0 && <Badge />}
Rendering a whole object

A plain object is not a valid React child. Pick the property you want to show.

✗ <div>{user}</div>
✓ <div>{user.name}</div>
Side effects during render

Render can run more than once, so anything with a side effect would run more than once too.

✗ function Profile() {
  saveVisit();
  return <h1>Profile</h1>;
}
✓ // call saveVisit() from an event handler or useEffect
Forgetting keys in map

React needs a stable key on each item a map returns. Lesson 9 covers why.

✗ {lessons.map((l) => <p>{l.title}</p>)}
✓ {lessons.map((l) => <p key={l.id}>{l.title}</p>)}

Best practices

  • Keep the left side of && a real boolean - compare counts and lengths explicitly.
  • Use && for something-or-nothing and a ternary for one-of-two.
  • Move complex conditions above the return, or into an early return.
  • Put formatting in small, pure functions and call them from JSX.
  • Never cause side effects while rendering.
  • Round or format computed numbers before showing them.

Practice

Write these yourself before opening anything. Getting them wrong first is most of how this sticks.

1.

Build a LessonCard for { title: "JSX Expressions", duration: 30, completed: true, quizQuestions: 10 } that shows the title, "Duration: 30 minutes", "Quiz: 10 questions", and Completed or Not Completed.

Show hint

Two outcomes means a ternary.

Show solution
const lesson = { title: "JSX Expressions", duration: 30, completed: true, quizQuestions: 10, }; function LessonCard() { return ( <article> <h2>{lesson.title}</h2> <p>Duration: {lesson.duration} minutes</p> <p>Quiz: {lesson.quizQuestions} questions</p> {lesson.completed ? <span>Completed</span> : <span>Not Completed</span>} </article> ); }
2.

Predict what each line renders, then check: {0 && "a"}, {"" && "b"}, {null ?? "c"}, {[1, 2, 3]}, {5 > 3 && "d"}.

Show hint

Work out the value of each expression first, then ask whether React shows that value.

Show solution
{0 && "a"} -> 0 (the value is 0, a number) {"" && "b"} -> nothing ("" renders nothing) {null ?? "c"} -> c (?? falls through on null) {[1, 2, 3]} -> 123 (arrays render item by item) {5 > 3 && "d"} -> d
3.

A Notifications component shows "{count} notifications" inside a span, but a lone 0 appears when there are none. Fix it.

Show hint

What does 0 && anything return?

Show solution
function Notifications({ count }: { count: number }) { return ( <div> {count > 0 && <span>{count} notifications</span>} </div> ); }
Coding challenge

A course progress card

Show progress for any number of completed lessons, without ever printing a stray 0.

It should
  • Take completedLessons and totalLessons as numbers.
  • Always show "X / Y lessons completed".
  • Show a rounded percentage.
  • Show "Course Started" only when at least one lesson is complete.
  • Show "Course Complete" instead when every lesson is complete.
  • Check it with 0, 1, and 10 completed out of 10.
function CourseProgress() { const completedLessons = 0; const totalLessons = 10; return ( <div> {/* your JSX here */} </div> ); }
Show one solution
function CourseProgress({ completedLessons, totalLessons, }: { completedLessons: number; totalLessons: number; }) { const percent = Math.round((completedLessons / totalLessons) * 100); const isComplete = completedLessons === totalLessons; return ( <div> <p> {completedLessons} / {totalLessons} lessons completed ({percent}%) </p> {isComplete ? ( <span>Course Complete</span> ) : ( completedLessons > 0 && <span>Course Started</span> )} </div> ); } // 0 of 10 -> "0 / 10 lessons completed (0%)" nothing else - not a stray 0 // 1 of 10 -> "1 / 10 lessons completed (10%)" Course Started // 10 of 10 -> "10 / 10 lessons completed (100%)" Course Complete

Key points

  • Braces hold one JavaScript expression - anything that produces a value.
  • Statements such as if and for cannot go inside braces; use &&, a ternary, or an early return.
  • Strings and numbers render, including 0 and NaN.
  • true, false, null, undefined, and "" render nothing.
  • A plain object is not a valid child; show one of its properties.
  • a && b returns a when a is falsy, so a numeric 0 on the left is printed.
  • Compare counts explicitly: {count > 0 && <Badge />}.
  • Braces work in attributes; a false boolean attribute is left off entirely.
  • Functions called during render should only calculate, never cause side effects.

Quick check before you move on

Why does {false} render nothing but {0} renders 0?
React ignores booleans on purpose so that conditions can live in JSX. 0 is a number, and React renders every number.
What does {count && <Badge />} render when count is 0?
0. The expression returns its falsy left side, which is the number 0, and React prints it.
Why can an if statement not go inside braces?
Braces compile to a function argument, which must be a value. if is a statement and produces no value.
What does <button disabled={false}> produce in the DOM?
A button with no disabled attribute at all.
How do you show a whole object while debugging?
Convert it to a string first, for example {JSON.stringify(user)}.

Interview questions

What is a JSX expression?

A JavaScript expression inside curly braces in JSX. It is evaluated on every render and its value becomes a child or an attribute value.

Can you use if inside JSX?

Not inside braces - if is a statement and braces need a value. Use && or a ternary inside the JSX, or put the if above the return, including as an early return.

Why does 0 render but false does not?

React deliberately renders booleans, null, and undefined as nothing so conditions can be written inline. 0 is a number, and numbers are always rendered.

What is wrong with {count && <Badge />}?

When count is 0, && returns 0, which React renders as the text 0. Make the left side a boolean: {count > 0 && <Badge />}.

Can you render an object directly?

No. React throws "Objects are not valid as a React child". Render a property, map an array of objects into elements, or use JSON.stringify for debugging.

Why should render logic be free of side effects?

React can call a component more than once - StrictMode does in development, and concurrent rendering can discard renders. Side effects in render would repeat or happen for UI that never appears. They belong in event handlers or effects.

&& or a ternary - when do you use each?

&& when the alternative is nothing, as long as the left side is a real boolean. A ternary when there are two things to show. For more than two, compute the result before the return or split the component.

Quiz

  1. 1.

    const value = 0; return <div>{value}</div>; What renders? A. Nothing B. 0 C. undefined D. An error

  2. 2.

    const value = false; return <div>{value}</div>; What renders? A. false B. 0 C. Nothing D. An error

  3. 3.

    Which is a valid JSX expression? A. {if (user)} B. {user.name} C. {for (...)} D. {const x = 10}

  4. 4.

    Which renders the badge only when count is above zero, without a stray 0? A. {count && <Badge />} B. {count > 0 && <Badge />}

  5. 5.

    const user = { name: "Chandu" }; return <div>{user}</div>; What happens? A. It shows Chandu B. It shows [object Object] C. An error: objects are not valid as a React child D. Nothing

  6. 6.

    What does {"" && <p>Hi</p>} render? A. Hi B. An empty string - nothing visible C. 0 D. An error

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...