//
Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
The essential building blocks every React basic learner should know before diving into advanced patterns.
className instead of class, htmlFor instead of for, and camelCase for event attributes like onClick.{variable} inside JSX to display dynamic values -- you can put any JavaScript expression inside, but not statements like if or for.useState(initialValue) to get a [value, setValue] pair -- updating state with the setter triggers a re-render.onClick={handleClick} -- pass the function reference, never call it with parentheses like onClick={handleClick()}..map(), every element needs a unique key so React can efficiently track additions, removals, and reorders.{condition ? <A /> : <B />} or {condition && <A />} since you cannot use if statements inside JSX directly.value={state} and onChange={e => setState(e.target.value)} together -- setting value alone makes the input read-only.children -- this is how you build wrapper and layout components.useEffect(() => { ... }, [deps]) for things that happen outside rendering like API calls, timers, or subscriptions.[] means "run once on mount" -- listing variables means "re-run when these change" -- omitting it means "run after every render."export default or export to make a component available, then import it in another file to use it -- the @/ alias points to your project root.<>...</> (Fragment) when you don't need an extra <div> -- this keeps your rendered HTML clean.style={{ color: "red", fontSize: "16px" }} with camelCase properties -- but in practice, use a CSS framework like Tailwind instead.useState.interface or type and use .tsx files -- the compiler will flag missing props, wrong types, and typos instantly.A condensed summary of the 25 most important best practices drawn from every page in this section.
function Parent() { function Child() {} /* bad - Child remounts every render */ }.props.items.push(x)) quietly mutates the parent's data and breaks React's rendering model; copy into new arrays or objects before modifying and let the parent own the state.React.ComponentPropsWithoutRef<"button"> so every native attribute stays in sync with the DOM API; re-declaring props manually drifts the moment the DOM spec changes.interface gives better error messages and supports declaration merging, so use it for component prop shapes; reserve type for unions, mapped types, and other transformations that interfaces cannot express.{count && <Badge />} renders the literal string "0" when count is 0 because 0 is falsy but still renderable; use count > 0 && <Badge /> or a ternary to avoid the infamous "0" bug.{ status: "success"; data } | { status: "error"; error } so TypeScript narrows inside each branch; this eliminates optional-chaining noise and makes unreachable branches obvious.<Spinner /> → <DataTable />, or <input> ↔ <textarea>) unmounts the old subtree and wipes its state; keep the same type and toggle props when you want state to persist.onClick={handleClick} attaches the function; onClick={handleClick()} calls it during render and wires the return value as the handler, which is almost never what you want - wrap in an arrow when you need to inject arguments: onClick={() => handleClick(id)}.onChange maps to the native input event, not native change, so it fires on every keystroke instead of on blur - use onBlur if you actually want "commit on blur" semantics.e.target is the element the event originated on (which may be a child), while e.currentTarget is the element the handler is attached to; read attributes you control from currentTarget to avoid surprises when clicking nested elements.{ passive: false } or window/document-level events; for Escape-key listeners, scroll monitors, or touchmove needing preventDefault, use useEffect + addEventListener on a ref and return a cleanup.value without a matching onChange makes the input read-only because React pins the DOM value to state; either pair the two - <input value={name} onChange={e => setName(e.target.value)} /> - or switch to defaultValue for an uncontrolled input.checked/defaultChecked, not value/defaultValue; mixing them up silently does the wrong thing since value on a checkbox is the submitted token, not the checked state: <input type="checkbox" checked={on} onChange={e => setOn(e.target.checked)} />.useFormStatus() only returns accurate pending info when called from inside a descendant of the <form>; calling it in the same component that renders the form returns stale defaults with no error, so extract the submit button into its own component.e.target.value is always a string - even for <input type="number"> - so wrap it with Number(...) or parseInt(...) before using it in math or storing typed state: onChange={e => setAge(Number(e.target.value))}.React.createElement, so HTML's class attribute is className, for is htmlFor, and styles take a camelCase object; using the HTML names compiles but warns and can silently drop styling in strict environments.{user} throws "Objects are not valid as a React child"; render a specific field like {user.name} or JSON.stringify(user) for debug output, and remember that 0 renders as text but null/false/undefined render nothing.children as React.ReactNode (which covers JSX, strings, numbers, arrays, null) and reserve React.JSX.Element for return types that always return a single JSX element.key must be unique among its siblings (not globally), and it must be stable across renders - use item.id, not Math.random() or Date.now(), or React will remount every row and destroy its state.index as key on lists that reorder, filter, or insert causes React to reuse the wrong DOM nodes, producing stale input values and broken focus; index keys are only safe for truly static lists.key on a component is the canonical way to reset all of its internal state - <PlayerProfile key={currentPlayerId} /> deliberately remounts when the player changes, which is a feature, not a hack.<>...</> shorthand does not accept a key prop; when mapping fragments, import Fragment and use <Fragment key={...}>...</Fragment> to avoid duplicate-key warnings or unkeyed renders.ref.current does not re-render the component - that is the point; reach for useRef when the UI does not depend on the value (timer IDs, observers, latest-value cache) and useState when it does.forwardRef is deprecated; just type ref as an ordinary prop with React.Ref<HTMLInputElement> and destructure it alongside the others - and remember {...props} now spreads ref too, so pull it off explicitly when passing through.useEffect, so wire observers as ref={node => { if (!node) return; const obs = new ResizeObserver(...); obs.observe(node); return () => obs.disconnect(); }} instead of the old null-check dance.Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥