Search across all documentation pages
289 pages across 30 sections. 3333 questions total.
TSX is JSX inside .tsx files with TypeScript type-checking enabled. The syntax is identical, but TSX validates prop types, expression types, and event handler types at compile time.
class is a reserved keyword in JavaScript. JSX compiles to JavaScript function calls, so React uses className to avoid the conflict. Using class triggers a console warning.
Any valid JavaScript expression — variables, ternaries, function calls, template literals, and array methods like .map(). Statements like if/else or for loops cannot go directly inside {}.
Use <>...</> when you need to return multiple sibling elements without adding an extra DOM node. Use <Fragment key={...}> when mapping a list that needs keys on the wrapper.
The style prop accepts a JavaScript object with camelCase properties, not a CSS string:
<div style={{ backgroundColor: "red", fontSize: 16 }} />Not directly. Use a ternary expression {condition ? <A /> : <B />} or extract the logic into a variable before the return statement.
<Button {...props} /> passes all properties of the props object as individual attributes to the component. This is useful for forwarding props or extending native HTML elements.
Use {/* comment */} inside JSX. HTML-style <!-- --> comments do not work and will cause a syntax error.
React.ReactNode is the broadest type — includes JSX, strings, numbers, null, undefined, and arraysReact.JSX.Element is a single JSX element only, no strings or nullReactNode for children props and JSX.Element for function return types that always return JSXJSX expressions compile to a single function call like React.createElement(...). Multiple adjacent elements would be multiple return values, which JavaScript doesn't support. Wrap siblings in a Fragment or a container element.
Use dangerouslySetInnerHTML={{ __html: htmlString }}. Only use this with sanitized, trusted content — it bypasses React's XSS protections.
They render nothing. This is why {condition && <Component />} works — when condition is false, false renders as empty. However, 0 renders as the text "0".
A component is a function that returns React.ReactNode (JSX, null, string, etc.) and is called by React during rendering. Regular functions are called by your code. Components must start with an uppercase letter so JSX treats them as components, not HTML elements.
Prefer interface for component props. Interfaces produce better TypeScript error messages and support declaration merging. Use type only when you need union types or mapped types.
Use destructuring defaults in the function signature:
function Button({ variant = "primary" }: ButtonProps) {
return <button className={variant}>Click</button>;
}children is a special prop containing whatever JSX is nested between a component's opening and closing tags. Type it as React.ReactNode. Use it for wrapper/layout components that don't know their content in advance.
Technically yes, but don't. React creates a new component identity each render, destroying all state in the inner component. Always define components at the module level.
Instead of a single children prop, accept multiple named props typed as React.ReactNode:
function Layout({ header, sidebar, children }: LayoutProps) {
return (
<div>
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}Use React.ComponentPropsWithoutRef<"element">:
interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> {
variant?: "primary" | "secondary";
}useState or useReducer)Server Components run only on the server, can await data directly, have zero client-side JavaScript, and cannot use hooks or event handlers. Regular (client) components run in the browser and support interactivity.
A utility type that adds children?: React.ReactNode to your props type:
type CardProps = React.PropsWithChildren<{ title: string }>;Use it when you want a shorthand instead of manually typing children.
Use a ternary {condition ? <A /> : <B />} to choose between two elements, or {condition && <A />} to show or hide one element. For complex multi-branch logic, use a variable or lookup object before the return.
JavaScript's && operator returns the first falsy value. 0 is falsy but is a valid React child that renders as text. Use {count > 0 && <Component />} or a ternary instead.
Map state values to JSX elements using a Record:
const statusUI: Record<Status, React.ReactNode> = {
idle: <p>Waiting...</p>,
loading: <Spinner />,
success: <DataTable />,
error: <ErrorBanner />,
};
return <div>{statusUI[status]}</div>;Yes, if the component type at a tree position changes (e.g., <Spinner /> to <DataTable />), React unmounts the old component and mounts a new one, destroying all state. If only props change on the same component type, React updates in place.
display: noneA TypeScript union where each member has a literal status field. The switch or if on status narrows the type, so TypeScript knows which fields are available in each branch — eliminating undefined checks.
Yes, they're hard to read. For more than two branches, extract the logic into a variable, use a switch statement, or use the lookup object pattern.
display: none when you want to keep the component mounted and preserve its state (e.g., tab panels)Return null. This is cleaner than returning an empty Fragment <></> and is the standard pattern for components that conditionally have no output.
Not directly inside JSX. Extract it to a variable or a helper function:
function getStatusIcon(status: Status) {
switch (status) {
case "success": return <CheckIcon />;
case "error": return <XIcon />;
default: return null;
}
}
return <div>{getStatusIcon(status)}</div>;Keys tell React which item is which across re-renders. Without stable keys, React cannot correctly match old and new elements, leading to lost state, broken animations, and incorrect DOM reuse.
Only for static lists that never reorder, filter, or have items inserted/removed. For any dynamic list, use a stable unique ID from your data (database ID, UUID, or slug).
React silently drops one of them, causing unpredictable behavior. Always ensure keys are unique among siblings. Combine fields if needed: key={`${item.type}-${item.id}`}.
Chain .filter() and .sort() before .map(). Wrap in useMemo if the list is large:
const visible = useMemo(
() => items.filter(i => i.active).sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
return <ul>{visible.map(i => <li key={i.id}>{i.name}</li>)}</ul>;setItems(prev => [...prev, newItem])setItems(prev => prev.filter(i => i.id !== id))setItems(prev => prev.map(i => i.id === id ? { ...i, done: true } : i))Always on the outermost element returned by the .map() callback. Placing it on an inner child element has no effect.
It generates a new value every render, so React treats every item as new — unmounting and remounting all components each time. This destroys state and kills performance.
Check items.length === 0 and render a placeholder message. An empty array renders nothing, which can look like a broken UI.
Import Fragment from React and use the named syntax:
import { Fragment } from "react";
{items.map(item => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</Fragment>
))}The short syntax <> does not support keys.
Change the key to a new value. React unmounts the old component and mounts a fresh one with initial state:
<PlayerProfile key={currentPlayerId} playerId={currentPlayerId} />When rendering more than a few hundred items causes visible jank. Use @tanstack/react-virtual to only render items currently in the viewport, keeping DOM size small.
A cross-browser wrapper around the native DOM event. React creates a SyntheticEvent for every event handler, normalizing browser differences. Access the native event via e.nativeEvent if needed.
e.currentTarget — the element the handler is attached toe.target — the element that actually triggered the event (may be a child)currentTarget when reading attributes of the element you attached the handler toAdding () invokes the function during render and passes its return value as the handler. Pass the reference instead: onClick={handleClick} or wrap it: onClick={() => handleClick(arg)}.
Wrap the handler in an arrow function:
<button onClick={() => deleteItem(item.id)}>Delete</button>Call e.preventDefault() in the onSubmit handler:
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>Use the React event types with the HTML element generic:
function handleClick(e: React.MouseEvent<HTMLButtonElement>) { ... }
function handleChange(e: React.ChangeEvent<HTMLInputElement>) { ... }
function handleSubmit(e: React.FormEvent<HTMLFormElement>) { ... }Yes. React's onChange behaves like the native input event, firing on every keystroke — not on blur like the native change event. This is by design for real-time form updates.
Call e.stopPropagation() in the child's event handler. For the capture phase (top-down), use onClickCapture instead of onClick.
In most cases, no. The inline arrow creates a new function each render, which can defeat React.memo on child components. Only optimize with useCallback in hot paths like large lists.
Use useEffect with addEventListener on window or document:
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape") closeModal();
};
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
}, []);Type it as a function accepting the data you need:
interface Props {
onSearch: (query: string) => void;
}Or use React's built-in handler type: React.MouseEventHandler<HTMLButtonElement>.
value + onChange — React is the source of truthdefaultValue — read it on demand with a ref or FormDataYou can pass an async function directly to <form action={fn}>. React calls it with a FormData object on submit, handles the pending state, and integrates with useActionState for managing return values — no e.preventDefault() needed.
useActionState(actionFn, initialState) returns [state, formAction, isPending]. On submit, it calls actionFn(prevState, formData) and updates state with the result. isPending is true while the action runs.
Setting value without an onChange handler makes the input uneditable — React locks the value to state. Either add onChange to update state, or switch to defaultValue for an uncontrolled input.
defaultValue only sets the initial DOM value on mount. Changes after mount have no effect. Use controlled value if React needs to drive updates, or add a key prop to force remount.
Use checked / defaultChecked instead of value / defaultValue:
<input
type="checkbox"
checked={isEnabled}
onChange={e => setIsEnabled(e.target.checked)}
/>e.target.value is always a string, even for <input type="number">. Parse it explicitly: Number(e.target.value) or parseInt(e.target.value, 10).
useFormStatus() returns { pending } to show loading state during a form action. It must be called in a component that is a child of a <form> — not in the same component that renders the form.
Use a single state object and a generic update function:
const [form, setForm] = useState({ name: "", email: "" });
function updateField(field: string, value: string) {
setForm(prev => ({ ...prev, [field]: value }));
}Use useOptimistic to immediately show the expected result while the action processes. If the action fails, React reverts to the actual state automatically.
<label htmlFor="id"> matching the input's idrequired, aria-describedby for error messages, and aria-invalid for invalid fieldsuseRef returns a mutable object { current: value } that persists across renders without causing re-renders. Use it for DOM access (focus, scroll, measure), storing timer IDs, and keeping mutable values that the UI doesn't depend on.
useState triggers a re-render when updated — use for values the UI displaysuseRef does not trigger re-renders — use for values like timer IDs, previous values, or DOM nodesDOM refs are populated after React mounts the element. During render, the DOM node doesn't exist yet. Access refs in event handlers, useEffect, or behind a null check: ref.current?.focus().
No. In React 19, ref is a regular prop. Accept it directly:
function Input({ ref, ...props }: { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}A ref callback is a function passed to the ref prop. React calls it with the DOM node on mount and null on unmount. In React 19, it can return a cleanup function. Use it when you need setup/teardown logic tied to the DOM node (like ResizeObserver).
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
timerRef.current = setInterval(() => { ... }, 1000);
// Later:
clearInterval(timerRef.current!);This avoids stale closure issues since ref.current always points to the latest value.
It lets you customize the value exposed when a parent uses a ref on your component. Instead of exposing the raw DOM node, you expose a limited API like { open(), close() }. Use sparingly — prefer props for most communication.
Yes. Increment ref.current in the component body:
const renderCount = useRef(0);
renderCount.current += 1;Since ref changes don't trigger re-renders, this won't cause an infinite loop.
React's StrictMode simulates unmount/remount in development, so the ref callback fires with null then the node. This is expected. In React 19, return a cleanup function from the ref callback instead of checking for null.
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T | undefined>(undefined);
useEffect(() => { ref.current = value; }, [value]);
return ref.current;
}The ref stores the old value because useEffect runs after render.