Volume I index
JSX Is Real React Code

Expressions Inside JSX

Ishtmeet Singh @ishtms/July 14, 2026/9 min read
#react#jsx#javascript#expressions#rendering

JSX gives markup a fixed structure and lets JavaScript calculate selected values inside it. Curly braces mark each JavaScript expression position.

jsx
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.

jsx
<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.

text
evaluate expression
  -> obtain JavaScript value
  -> include value in element props
  -> React renders supported node values

The expression runs whenever the component renders. It should calculate from current props, state, context, or local constants without changing external systems.

A Statement Controls Execution

An if statement does not produce a value for a JSX position.

jsx
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.

jsx
const label = complete ? "Complete" : "In progress";
return <p>{label}</p>;

Or use statements before the returned JSX.

jsx
let label;

if (complete) {
  label = "Complete";
} else {
  label = "In progress";
}
jsx
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.

jsx
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.

jsx
<p>{total} complete</p>

When adjacent pieces create awkward whitespace, include an explicit string expression.

jsx
<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.

jsx
<progress max="6" />

Braces supply a JavaScript value.

jsx
<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.

jsx
<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.

jsx
<LessonCard lesson={lesson} />
jsx
<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.

jsx
<p>{"ReactBook"}</p>
<p>{19.2}</p>

React 19 also supports rendering a BigInt as text.

jsx
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.

jsx
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.

jsx
<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.

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.

jsx
<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.

jsx
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.

jsx
return <p>{lesson.title}</p>;

For temporary debugging, JSON can produce a string.

jsx
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.

jsx
const publishedAt = new Date();
return <p>{publishedAt}</p>;

Format the date into a string.

jsx
const label = publishedAt.toLocaleDateString();
return <p>{label}</p>;

Functions Are Values but Not Visible Children

An event prop needs a function value.

jsx
<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.

jsx
<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.

jsx
<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.

jsx
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.

jsx
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.

jsx
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.

jsx
<div dangerouslySetInnerHTML={{ __html: trustedHtml }} />

The API bypasses normal text escaping. Untrusted HTML can execute script through event attributes, malicious URLs, and other browser behaviors.

Comments Need a JavaScript Expression Position

This is not a JSX comment.

jsx
<main>
  // chapter heading
  <ChapterHeading />
</main>

The slash characters can become visible text because they appear inside JSX content.

Use a block comment inside braces.

jsx
<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.

jsx
<p>{lessons.filter((lesson) => lesson.published).map((lesson) => lesson.title).join(", ")}</p>

Named intermediate values expose each result.

jsx
const publishedLessons = lessons.filter((lesson) => lesson.published);
const titles = publishedLessons.map((lesson) => lesson.title);
const label = titles.join(", ");
jsx
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.

jsx
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.

jsx
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.

text
which JavaScript value does this expression produce
how does React handle that value in this position

Strings, 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