Modern JavaScript Syntax in Components
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.
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.
const title = lesson.title;
const completed = lessons.filter((lesson) => lesson.complete);const prevents reassignment of the binding.
title = "Another title";It does not make an object or array deeply immutable.
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.
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.
function LessonCard(props) {
return <h2>{props.title}</h2>;
}Object destructuring reads selected properties into local bindings.
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.
<LessonCard title="JSX" minutes={12} />
<LessonCard minutes={12} title="JSX" />Rename a property locally with a separator inside the pattern.
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.
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.
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.
const { author: { name } } = lesson;This throws when author is undefined. Direct property reads with explicit checks are clearer when nested data can be absent.
const authorName = lesson.author?.name ?? "Unknown author";Destructure Arrays by Position
Array destructuring uses positions rather than property names.
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.
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.
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.
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.
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.
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.
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.
Later object properties replace earlier properties with the same key.
Object Spread Is Shallow
Copying the top-level object retains references to nested objects.
const lesson = {
title: "Modern JavaScript",
author: { name: "Ishtmeet" },
};const nextLesson = { ...lesson };Both objects refer to the same nested author object.
nextLesson.author.name = "Another name";That mutation also changes lesson.author.name.
Copy every level that needs a changed property.
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.
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.
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.
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.
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.
const city = lesson.author?.address.city;This protects a missing author, but it still throws when author exists and address is missing.
Optional chaining handles null and undefined. It does not prove that present data has the expected type or content.
Nullish Coalescing Preserves Valid Falsy Values
Use ?? to supply a fallback for null or undefined.
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.
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.
const label = `Lesson ${current} of ${total}`;Use the string in JSX.
return <p>{label}</p>;Template strings also build URLs from controlled path values.
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.
<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.
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.
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.
const nextLessons = lessons.map((lesson) => (
{ ...lesson, selected: false }
));Without parentheses, braces start a function block.
const nextLessons = lessons.map((lesson) => {
...lesson,
selected: false
});That source is invalid and does not return an object.
An explicit return also works.
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.
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.
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.
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.
const rawName = lesson.author?.name;
const trimmedName = rawName?.trim();
const displayName = trimmedName || "Unknown author";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.
Syntax should make the data decision easier to see. Split an expression when several absence and formatting rules have been combined.
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.
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 rulesSource Notes
- MDN, Destructuring assignment
- MDN, Spread syntax
- MDN, Optional chaining
- MDN, Nullish coalescing
- ECMA-262, ECMAScript language specification