Volume I index
JSX Is Real React Code

Modern JavaScript Syntax in Components

Ishtmeet Singh @ishtms/July 14, 2026/10 min read
#react#javascript#destructuring#spread#syntax

Most syntax inside a React component comes from JavaScript. React supplies component and Hook behavior. JavaScript supplies variables, functions, objects, arrays, destructuring, property access, modules, and operators.

jsx
export default function LessonCard({ lesson, compact = false }) {
  const subtitle = lesson.subtitle ?? "No subtitle";

  return (
    <article className={compact ? "card compact" : "card"}>
      <h2>{lesson.title}</h2>
      <p>{subtitle}</p>
    </article>
  );
}

Only the component rendering and JSX element model are React concerns. Parameter destructuring, a default value, const, property access, nullish coalescing, and the conditional expression are JavaScript.

This separation helps when reading documentation. Look up ?? in a JavaScript reference and state preservation in a React reference.

Prefer const for One Binding

Use const when the binding will not be assigned another value.

jsx
const title = lesson.title;
const completed = lessons.filter((lesson) => lesson.complete);

const prevents reassignment of the binding.

jsx
title = "Another title";

It does not make an object or array deeply immutable.

jsx
const lesson = { complete: false };
lesson.complete = true;

The binding still refers to the same object, so the property mutation is allowed by JavaScript. React rendering rules still require props and state data to remain unmodified.

Use let when the local binding must receive another value.

jsx
let label;

if (complete) {
  label = "Complete";
} else {
  label = "In progress";
}

Avoid var in current component source. Its function scope and hoisting behavior make local binding lifetimes less direct than block-scoped const and let.

Destructure Object Properties

Props arrive as one object.

jsx
function LessonCard(props) {
  return <h2>{props.title}</h2>;
}

Object destructuring reads selected properties into local bindings.

jsx
function LessonCard({ title, minutes }) {
  return <h2>{title}, {minutes} min</h2>;
}

The pattern requires property names, not property order. These two elements produce props with the same names.

jsx
<LessonCard title="JSX" minutes={12} />
<LessonCard minutes={12} title="JSX" />

Rename a property locally with a separator inside the pattern.

jsx
const { title: lessonTitle } = lesson;

The object property remains title. The local binding becomes lessonTitle.

This separator does not create a TypeScript annotation. The source is ordinary JavaScript object-pattern syntax.

Defaults Apply Only to undefined

Supply a default in the destructuring pattern.

jsx
function LessonCard({ minutes = 10 }) {
  return <p>{minutes} min</p>;
}

The default applies when minutes is omitted or supplied as undefined.

It does not replace null, zero, false, or an empty string.

text
undefined -> 10
null      -> null
0         -> 0
false     -> false
""        -> ""

Each of those values can have a distinct meaning. Do not use a default as broad data validation.

Nested destructuring can read several levels at once.

jsx
const { author: { name } } = lesson;

This throws when author is undefined. Direct property reads with explicit checks are clearer when nested data can be absent.

jsx
const authorName = lesson.author?.name ?? "Unknown author";

Destructure Arrays by Position

Array destructuring uses positions rather than property names.

jsx
const coordinates = [12, 24];
const [x, y] = coordinates;

The first binding receives position zero. The second receives position one.

React's useState Hook returns a two-position array.

jsx
const [count, setCount] = useState(0);

The syntax itself is JavaScript. React defines the values stored at the two positions.

Skip an array position with an empty slot only when the API's positional contract is already clear.

jsx
const [, secondLesson] = lessons;

Named variables or direct index access can be easier to read when several positions are skipped.

Rest Collects Remaining Values

Object rest syntax gathers properties not already selected.

jsx
function TextField({ label, id, ...inputProps }) {
  return (
    <label htmlFor={id}>
      {label}
      <input id={id} {...inputProps} />
    </label>
  );
}

label and id become local bindings. inputProps becomes a new object containing the remaining enumerable own properties.

The component then spreads those properties onto the host input. Properties written after the spread override properties with the same name.

Rest must be the final entry in the object pattern.

jsx
const { ...rest, title } = lesson;

That source is invalid because JavaScript cannot know which properties remain before later selections are complete.

Spread Copies Properties into Another Object

Spread syntax inside an object literal copies enumerable own properties.

jsx
const nextLesson = {
  ...lesson,
  complete: true,
};

Order controls repeated properties. The later complete entry replaces the copied value.

Reverse the order and the source object's property wins.

jsx
const nextLesson = {
  complete: true,
  ...lesson,
};

If lesson.complete is false, nextLesson.complete becomes false.

Use ordering as an explicit part of the update. Put the values that should win after the spread.

Object Spread Is Shallow

Copying the top-level object retains references to nested objects.

jsx
const lesson = {
  title: "Modern JavaScript",
  author: { name: "Ishtmeet" },
};
jsx
const nextLesson = { ...lesson };

Both objects refer to the same nested author object.

jsx
nextLesson.author.name = "Another name";

That mutation also changes lesson.author.name.

Copy every level that needs a changed property.

jsx
const nextLesson = {
  ...lesson,
  author: {
    ...lesson.author,
    name: "Another name",
  },
};

The copied levels are new objects. Other nested references remain shared.

This same copy pattern is useful whenever an object needs one changed property without mutating the original object.

Spread Copies Arrays Too

Array spread produces a new array containing the current item references.

jsx
const nextLessons = [...lessons, newLesson];

The source lessons array is unchanged. newLesson appears at the end of the next array.

Insert at the start with another order.

jsx
const nextLessons = [newLesson, ...lessons];

Again, nested objects are shared. Array spread copies the collection positions, not the objects stored at those positions.

Use map, filter, and copying array methods for collection transformations. Do not copy an array and then mutate its item objects during render.

Optional Chaining Stops on Nullish Values

Optional chaining checks the value immediately to its left.

jsx
const authorName = lesson.author?.name;

If author is null or undefined, the expression produces undefined instead of attempting .name and throwing.

If author exists, JavaScript reads name normally.

Optional chaining can continue through several supported optional levels.

jsx
const city = lesson.author?.address?.city;

Use each ?. where the value on its left can be absent. One optional access does not protect every later property.

jsx
const city = lesson.author?.address.city;

This protects a missing author, but it still throws when author exists and address is missing.

Nullish Coalescing Preserves Valid Falsy Values

Use ?? to supply a fallback for null or undefined.

jsx
const authorName = lesson.author?.name ?? "Unknown author";

The fallback is selected only when the left value is nullish.

The || operator selects its right value for every falsy left value.

jsx
const countWithOr = count || "Not counted";
const countWithNullish = count ?? "Not counted";

When count is zero, the first result is Not counted while the second remains zero.

This distinction also applies to an empty string and false. Use ?? when those values are valid data and only absence needs a fallback.

Use || when the product rule truly treats every falsy value as absent.

Template Strings Combine Text and Expressions

Backticks create a template string. ${...} inserts a JavaScript expression.

jsx
const label = `Lesson ${current} of ${total}`;

Use the string in JSX.

jsx
return <p>{label}</p>;

Template strings also build URLs from controlled path values.

jsx
const href = `/learn/${chapter.slug}/${lesson.slug}`;

Do not place untrusted values into URLs without validating the URL policy. A string that starts with an unsafe scheme can create a security problem even when template syntax is correct.

Inside JSX attributes, the outer braces belong to JSX and the backticks belong to JavaScript.

jsx
<a href={`/learn/${lesson.slug}`}>{lesson.title}</a>

Computed Property Names Use an Expression as a Key

A form object can update the field named by an input.

jsx
const fieldName = "weeklyGoal";
const nextForm = {
  ...form,
  [fieldName]: "3",
};

The brackets evaluate fieldName and use its result as the object property key. The result has a weeklyGoal property.

Without brackets, the object would create a property literally named fieldName.

jsx
const nextForm = {
  fieldName: "3",
};

Use a computed property name only when one shared operation is clearer than several explicit assignments. Separate assignments remain clearer for a small form.

Arrow Functions Can Return Objects

An arrow callback returning an object needs parentheses around the object literal.

jsx
const nextLessons = lessons.map((lesson) => (
  { ...lesson, selected: false }
));

Without parentheses, braces start a function block.

jsx
const nextLessons = lessons.map((lesson) => {
  ...lesson,
  selected: false
});

That source is invalid and does not return an object.

An explicit return also works.

jsx
const nextLessons = lessons.map((lesson) => {
  return { ...lesson, selected: false };
});

Use the form that leaves the transformation readable.

Keep Module Syntax Distinct

import and export are modern JavaScript syntax too. Their braces describe module bindings, not JSX expressions or object destructuring.

jsx
import LessonCard from "./LessonCard.jsx";
export default function LessonList() {}

Do not reinterpret braces in named imports as JSX or object destructuring. Similar punctuation can belong to different grammar positions.

jsx
import { LessonCard } from "./lesson-ui.jsx";

This requests a named module export. It does not read a property from a runtime props object.

Prefer Intermediate Values to Dense Syntax

Modern syntax can compress several operations.

jsx
return <p>{lesson.author?.name?.trim().toUpperCase() ?? "UNKNOWN"}</p>;

The expression hides decisions about missing authors, an empty name, whitespace, and display casing.

Name the steps that carry product meaning.

jsx
const rawName = lesson.author?.name;
const trimmedName = rawName?.trim();
const displayName = trimmedName || "Unknown author";
jsx
return <p>{displayName}</p>;

The use of || is deliberate here because an empty trimmed string should select the fallback. The intermediate bindings expose that rule.

Use Standardized Syntax

Destructuring, rest and spread, optional chaining, nullish coalescing, template strings, and computed properties are standardized JavaScript features.

Do not place a draft proposal into application code without checking its proposal stage, tool support, browser output, and fallback. Vite can transform selected syntax, but a transform does not supply every missing runtime API automatically.

Check the JavaScript Layer

The application now uses a compact set of JavaScript operations inside and around JSX.

text
expressions produce child and prop values
React DOM interprets host props
fragments group without a DOM wrapper
array methods produce sibling nodes
keys retain sibling identity
modern JavaScript syntax reads and copies data without changing React's rules

Source Notes