Expressions Inside JSX
JSX gives markup a fixed structure and lets JavaScript calculate selected values inside it. Curly braces mark each JavaScript expression position.
function LessonHeading({ lesson, total }) {
return (
<h1>Lesson {lesson} of {total}</h1>
);
}The text outside braces remains JSX text. React evaluates lesson and total during rendering and places their results among the heading's children.
The braces do not print. They are source syntax that tells the JSX transform where one JavaScript expression begins and ends.
An Expression Produces a Value
JavaScript expressions have a result.
<p>{2 + 3}</p>
<p>{title.toUpperCase()}</p>
<p>{lesson.minutes}</p>The first produces the number 5. The second calls a method and produces a string. The third reads an object property.
React receives each result as a child value for the paragraph element.
evaluate expression
-> obtain JavaScript value
-> include value in element props
-> React renders supported node valuesThe expression runs whenever the component renders. It should calculate from current props, state, context, or local constants without changing external systems.
Braces hold one JavaScript expression. React renders the value produced by that expression.
A Statement Controls Execution
An if statement does not produce a value for a JSX position.
function Status({ complete }) {
return <p>{if (complete) "Complete"}</p>;
}The file cannot be parsed because the braces require an expression.
Use an expression that produces the required value.
const label = complete ? "Complete" : "In progress";
return <p>{label}</p>;Or use statements before the returned JSX.
let label;
if (complete) {
label = "Complete";
} else {
label = "In progress";
}return <p>{label}</p>;The if statement chooses which assignment runs. The later JSX expression reads the resulting string.
Loops are statements too. Use them before the return to build data, or use array methods such as map and filter, which return new values.
Braces Work in Element Content
Place an expression among child content.
const completed = 2;
const total = 6;
return <p>{completed} of {total} complete</p>;JSX preserves the surrounding text and inserts text nodes for the numbers.
Whitespace follows JSX text rules. This source includes a literal space before complete because the text segment contains one.
<p>{total} complete</p>When adjacent pieces create awkward whitespace, include an explicit string expression.
<strong>{completed}</strong>{" "}
<span>lessons complete</span>Do not insert {" "} between every element by habit. CSS layout with gap is more appropriate for many visual spacing needs. The string is for text separation in a sentence.
Braces Work in Prop Values
Quoted JSX prop values are strings.
<progress max="6" />Braces supply a JavaScript value.
<progress max={total} />Here max receives a number. The browser may serialize either form similarly, but JavaScript component props retain their supplied types.
Do not put quotes around a braced expression.
<progress max="{total}" />That prop receives the literal string {total}. The quotes prevent JSX from entering a JavaScript expression position.
Pass an object, function, or array through braces as well.
<LessonCard lesson={lesson} /><button onClick={handleClick}>Continue</button>The first prop holds an object reference. The second holds a function reference. Rendering those values directly as children has different rules.
Strings, Numbers, and BigInts Become Text
React can render strings and numbers as text nodes.
<p>{"ReactBook"}</p>
<p>{19.2}</p>React 19 also supports rendering a BigInt as text.
const exactCount = 9007199254740993n;
return <p>{exactCount}</p>;Use BigInt only when the data actually requires integer precision beyond normal number safety and the surrounding APIs support it. JSON serialization does not accept BigInt without custom handling.
NaN and Infinity are numbers and can appear as text. Their presence often indicates a calculation or parsing case that the UI should handle explicitly.
const average = totalMinutes / lessonCount;When lessonCount is zero, the result can be Infinity or NaN. Decide on empty-state text before rendering the calculation.
Booleans and Empty Values Produce No Text
React renders no host text for true, false, null, or undefined as children.
<p>{false}</p>
<p>{null}</p>
<p>{undefined}</p>The paragraph elements still exist. Their expression positions produce no child text.
This behavior powers optional JSX.
{isNew && <span>New</span>}False produces an empty React child, while zero remains visible because it is a number. A condition that can return either value must account for this difference.
To display a boolean as text, convert it or choose a label.
<p>{complete ? "true" : "false"}</p>For normal interfaces, a descriptive label such as Complete communicates more than the raw word true.
Plain Objects Are Not React Children
This object cannot be rendered directly.
const lesson = {
title: "Expressions Inside JSX",
};
return <p>{lesson}</p>;React reports that an object is not valid as a React child. The object has no defined text representation for the UI.
Render a property.
return <p>{lesson.title}</p>;For temporary debugging, JSON can produce a string.
return <pre>{JSON.stringify(lesson, null, 2)}</pre>;Do not use JSON output as normal product UI. It exposes implementation fields and does not provide labels or formatting for readers.
Dates are objects too.
const publishedAt = new Date();
return <p>{publishedAt}</p>;Format the date into a string.
const label = publishedAt.toLocaleDateString();
return <p>{label}</p>;Passing an object as a prop is supported. Rendering that object itself as a child is not.
Functions Are Values but Not Visible Children
An event prop needs a function value.
<button onClick={handleClick}>Continue</button>The function is stored in a prop that React DOM recognizes as an event handler. It is not rendered as text.
Putting a function among children does not call it.
<p>{formatTitle}</p>React reports an invalid function child or ignores unsupported placement depending on the context and version. Call the function when its result is the intended child.
<p>{formatTitle(title)}</p>The function should return a supported React node value. The call runs during rendering, so it must remain a pure calculation.
Property Access Can Fail Before React Receives a Value
React cannot render a fallback if JavaScript throws while evaluating the expression.
function AuthorName({ lesson }) {
return <p>{lesson.author.name}</p>;
}If lesson.author is undefined, JavaScript throws before React receives a child value.
Check the data or use optional access when absence is supported.
const authorName = lesson.author?.name ?? "Unknown author";
return <p>{authorName}</p>;Optional chaining stops the property lookup when an intermediate value is nullish. Nullish coalescing then supplies a defined display string before JSX receives it.
Do not add optional chaining to every property path. Required data should fail validation at its entry point rather than silently turning into blank UI.
JSX Escapes Rendered Text
Suppose a lesson comment contains markup characters.
const comment = "<strong>Read this</strong>";
return <p>{comment}</p>;React DOM inserts text. The browser displays the angle brackets rather than creating a strong element.
This protects normal data from being interpreted as HTML. It also means a trusted markup string does not become nested UI through braces.
React provides dangerouslySetInnerHTML for deliberate HTML insertion.
<div dangerouslySetInnerHTML={{ __html: trustedHtml }} />The API bypasses normal text escaping. Untrusted HTML can execute script through event attributes, malicious URLs, and other browser behaviors.
Do not use dangerouslySetInnerHTML for normal text. HTML from users or external systems needs a reviewed sanitization policy before insertion.
Comments Need a JavaScript Expression Position
This is not a JSX comment.
<main>
// chapter heading
<ChapterHeading />
</main>The slash characters can become visible text because they appear inside JSX content.
Use a block comment inside braces.
<main>
{/* Chapter heading */}
<ChapterHeading />
</main>The braces enter JavaScript and the block comment produces no value.
Comments should explain a non-obvious constraint. A comment that repeats the next element name adds no information and should be removed.
Move Dense Expressions Above JSX
This expression performs several operations inside one child position.
<p>{lessons.filter((lesson) => lesson.published).map((lesson) => lesson.title).join(", ")}</p>Named intermediate values expose each result.
const publishedLessons = lessons.filter((lesson) => lesson.published);
const titles = publishedLessons.map((lesson) => lesson.title);
const label = titles.join(", ");return <p>{label}</p>;The JSX now shows the UI position. The JavaScript above shows the calculation. A debugger can pause between steps, and an empty-state branch can use publishedLessons.length without repeating the filter.
Build a Display Calculation
Read the calculation inputs from props.
function LessonProgress({ completed, total }) {
const percentage = Math.round((completed / total) * 100);
const label = `${completed} of ${total} complete`;
return (
<section>
<p>{label}</p>
<progress value={completed} max={total} />
<p>{percentage}%</p>
</section>
);
}The calculations occur before the return. JSX expressions place the resulting string and numbers in specific UI positions. No extra state is needed because all three values follow directly from props.
Handle a zero total before dividing if the component supports it.
const percentage = total === 0
? 0
: Math.round((completed / total) * 100);The component now produces a finite number for the empty course case.
Lesson Check
Each JSX expression should now answer two questions.
which JavaScript value does this expression produce
how does React handle that value in this positionStrings, numbers, and BigInts become text. Empty values and booleans produce no text. Elements describe nested React work. Plain objects are invalid children. Pass functions as callback props or call them to obtain a renderable result.
Source Notes
- React documentation, JavaScript in JSX with Curly Braces
- React documentation, Passing Props to a Component
- React 19 release notes, BigInt rendering
- React DOM reference, Common components