React Fundamentals
1. What is React and what problem does it solve?
React is a JavaScript library for building user interfaces, developed and maintained by Meta. It solves the problem of efficiently updating the DOM when application state changes. Before React, developers manually manipulated the DOM, which was error-prone and difficult to maintain as applications grew. React introduces a component-based architecture where UIs are built as a tree of reusable components, a declarative programming model (describe what the UI should look like, not how to update it), and a virtual DOM that minimises expensive real DOM operations by batching and optimising updates.
2. What is the difference between a library and a framework?
React is a library — it handles only the view layer (UI rendering) and leaves decisions about routing, state management, HTTP requests, and project structure to the developer. Angular is a full framework that prescribes solutions for all these concerns out of the box. Vue sits between the two. The library approach gives React flexibility and a smaller footprint but requires assembling a stack (React + React Router + Redux/Zustand + React Query, etc.). The framework approach is more opinionated but provides a complete, integrated solution with less decision fatigue, which suits larger teams needing consistency.
3. What is JSX?
JSX (JavaScript XML) is a syntax extension for JavaScript that allows writing HTML-like markup directly in JavaScript files. Babel compiles JSX to React.createElement() calls: <div className="app">Hello</div> becomes React.createElement('div', { className: 'app' }, 'Hello'). JSX is not required to use React but is the standard because it is more readable. Key differences from HTML: class becomes className, for becomes htmlFor, all tags must be closed, and JavaScript expressions are embedded with curly braces {}. React 17+ introduced a new JSX transform that no longer requires importing React in every file.
4. What is the virtual DOM and how does React use it?
The virtual DOM is a lightweight in-memory representation of the real DOM. When state changes, React creates a new virtual DOM tree, compares it with the previous one using a diffing algorithm (reconciliation), identifies the minimum set of changes needed, and applies only those to the real DOM. This process is called reconciliation. The key insight is that real DOM operations (layout, paint, reflow) are expensive, while JavaScript object manipulation is cheap. React batches multiple state updates and applies them in a single DOM update cycle, reducing reflows and repaints. React 18's concurrent features further improve this by prioritising urgent updates.
5. What is a React component?
A React component is a reusable, independent piece of UI that accepts inputs (props) and returns JSX describing what should appear on screen. Components can be class-based (legacy) or function-based (modern). Function components are plain JavaScript functions: function Button({ label, onClick }) { return <button onClick={onClick}>{label}</button>; }. Components must return a single root element (or a Fragment). Component names must start with a capital letter to distinguish them from native HTML elements. Components compose to form a component tree that represents the entire application UI.
6. What are props?
Props (properties) are the mechanism for passing data from a parent component to a child component. They are read-only — a component must never modify its own props. Props can be any JavaScript value: strings, numbers, booleans, objects, arrays, functions, or other React elements. Destructuring in the function signature is the common pattern: function Card({ title, description, onClose }) { ... }. Default prop values are set with default parameter syntax: function Card({ title = 'Untitled' }) { ... }. Prop drilling (passing props through many layers) is a common pain point that context or state management libraries solve.
7. What is state in React?
State is mutable data managed by a component that, when changed, triggers a re-render. Unlike props (controlled by the parent), state is controlled by the component itself. useState manages local component state: const [count, setCount] = useState(0). State updates are asynchronous and batched — never mutate state directly; always use the setter function. When the new state depends on the previous state, use the functional update form: setCount(prev => prev + 1). Lifting state up (moving state to the nearest common ancestor) is the pattern for sharing state between sibling components.
8. What is the difference between controlled and uncontrolled components?
A controlled component has its form input value controlled by React state. The value is set via a prop and changes are handled via onChange: <input value={name} onChange={e => setName(e.target.value)} />. The React state is the single source of truth. An uncontrolled component stores its own state in the DOM — accessed via a ref: <input ref={inputRef} /> then inputRef.current.value. Controlled components are the recommended approach as they make data flow explicit, enable validation, and integrate with state management. Uncontrolled components are used for file inputs and when integrating with non-React code.
9. What is the React component lifecycle?
In function components (with hooks): mounting phase — component renders for the first time and useEffect with empty deps [] runs after the first render. Updating phase — component re-renders when props or state change and useEffect with deps runs after renders where the deps changed. Unmounting phase — the cleanup function returned from useEffect runs when the component unmounts. In class components (legacy): constructor → render → componentDidMount → shouldComponentUpdate → render → componentDidUpdate → componentWillUnmount. Function components with hooks are the modern standard.
10. What is a React Fragment?
A React Fragment (<>...</> or <React.Fragment>...</React.Fragment>) allows grouping multiple elements without adding an extra DOM node. Components must return a single root element — before Fragments, developers wrapped everything in unnecessary <div> elements that polluted the DOM structure and broke CSS (e.g., flexbox/grid layouts where the wrapper div was unintended). <> is the shorthand syntax. The longer <React.Fragment key={id}> form is needed when rendering a list that requires a key prop. Fragments have no visual representation in the DOM — they are purely a React concept.
React Hooks
11. What is useState?
useState is the hook for managing local state in a function component. It returns a tuple: the current state value and a setter function. const [value, setValue] = useState(initialValue). The initial value can be a primitive, object, array, or a function (lazy initialisation — useState(() => expensiveComputation()) only runs once). Setting state schedules a re-render; the component re-renders with the new state value. Multiple state values use multiple useState calls. State updates in event handlers are batched in React 18; flushSync forces synchronous updates when needed.
12. What is useEffect and when is it used?
useEffect lets you synchronise a component with external systems (data fetching, subscriptions, timers, manual DOM manipulation). It runs after the render is committed to the screen. The dependency array controls when it runs: empty array [] runs once after mount (like componentDidMount), no array runs after every render, and specific deps [a, b] runs when a or b changes. The returned cleanup function runs before the next effect and on unmount — use it to clear timers, cancel subscriptions, or abort fetch requests. React 18 + Strict Mode runs effects twice in development to help catch missing cleanups.
13. What is useContext?
useContext consumes a React Context, allowing any component in the tree to access shared data without prop drilling. Create a context with const ThemeContext = React.createContext(defaultValue). Wrap the tree with <ThemeContext.Provider value={theme}>. Any nested component reads it with const theme = useContext(ThemeContext). When the context value changes, all consumers re-render. Context is ideal for global data (theme, current user, locale) but should not replace dedicated state management for complex frequently-updating state, as every consumer re-renders on any context change — which can cause performance issues without memoisation.
14. What is useRef?
useRef returns a mutable ref object whose .current property is initialised to the passed value and persists for the full lifetime of the component without triggering re-renders when changed. Two main uses: (1) Accessing DOM nodes directly: const inputRef = useRef(null); <input ref={inputRef}> then inputRef.current.focus(). (2) Storing mutable values that should not trigger re-renders — previous prop values, timer IDs, abort controllers, or instance variables. Unlike useState, mutating ref.current is synchronous and does not cause a re-render. useRef is the escape hatch for imperative code.
15. What is useMemo and useCallback?
useMemo memoises the result of an expensive calculation: const sorted = useMemo(() => items.sort(compareFn), [items]). The computation re-runs only when items changes. useCallback memoises a function reference: const handleClick = useCallback(() => doSomething(id), [id]). The function is only recreated when id changes. Both are optimisation hooks to avoid unnecessary recalculation or re-creation. They are only needed when: (1) passing the memoised value or function to a component wrapped in React.memo, or (2) the computation is genuinely expensive. Premature memoisation adds complexity without benefit — profile first.
16. What is useReducer?
useReducer is an alternative to useState for complex state logic. const [state, dispatch] = useReducer(reducer, initialState). The reducer is a pure function: (state, action) => newState. Actions are dispatched with dispatch({ type: 'INCREMENT', payload: 5 }). useReducer is preferred over useState when state transitions involve multiple sub-values, the next state depends on complex logic on the current state, or you want to separate state logic from the component (testable reducer functions). It is the conceptual foundation of Redux and makes state transitions predictable and auditable.
17. What are custom hooks?
Custom hooks are functions starting with use that encapsulate and reuse stateful logic across components. They can use other hooks internally. Example: function useWindowSize() { const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight }); useEffect(() => { const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight }); window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []); return size; }. Custom hooks extract logic from components — when two components share the same state logic, extract it into a custom hook rather than duplicating code or using render props.
18. What is the useLayoutEffect hook?
useLayoutEffect has the same signature as useEffect but fires synchronously after all DOM mutations and before the browser paints. It is used for reading DOM layout (getBoundingClientRect) and synchronously making DOM mutations that must be reflected before the user sees the painted result. If you use useLayoutEffect to update state, React will flush that state and re-render synchronously before painting, preventing flickering. In most cases, useEffect is preferred as it does not block the browser from painting. useLayoutEffect is the equivalent of componentDidMount and componentDidUpdate in class components.
19. What is the useId hook?
useId generates unique, stable IDs that are consistent between server and client renders, solving the hydration mismatch problem when using auto-generated IDs for accessibility attributes. const id = useId() generates an ID like :r0:. It is used for associating form labels with inputs: <label htmlFor={id}>Name</label><input id={id} />. Each useId call in the same component produces a unique ID; multiple IDs can be derived from one call with suffixes: ${id}-first-name and ${id}-last-name. Do not use useId for list keys — use data IDs instead.
20. What is useTransition?
useTransition is a React 18 hook that marks a state update as non-urgent, allowing React to interrupt and defer it while keeping the UI responsive. const [isPending, startTransition] = useTransition(). Wrapping a state update in startTransition(() => setState(newValue)) tells React this update can be interrupted if a more urgent update (like a user typing) arrives. isPending is true while the transition is in progress — use it to show a loading indicator. useTransition is used for slow renders like filtering large lists, navigating between pages, or any update where the new state takes time to render.
State Management
21. What is prop drilling and how do you solve it?
Prop drilling is the pattern of passing props through multiple layers of components that don't use the data themselves — just to reach a deeply nested component that does. It makes code hard to maintain because every intermediate component must accept and forward the prop. Solutions: React Context (built-in, good for low-frequency updates), state management libraries (Redux, Zustand, Jotai for complex global state), component composition (render children directly to avoid passing props through intermediaries), or URL state (React Router for routing state). The appropriate solution depends on update frequency, access pattern, and application complexity.
22. What is Redux and when would you use it?
Redux is a predictable state management library based on a single immutable global store, pure reducer functions, and explicit actions. Its three principles: single source of truth (one store), state is read-only (changed only by dispatching actions), and changes are made with pure functions (reducers). Redux Toolkit (RTK) is the modern, official way to use Redux — it includes createSlice, createAsyncThunk, configureStore, and RTK Query. Use Redux for: large applications with complex state shared across many components, teams requiring a strict, auditable state pattern, or applications needing time-travel debugging with Redux DevTools. For simpler needs, Zustand or Context is lighter.
23. What is React Query (TanStack Query)?
React Query is a server-state management library that handles data fetching, caching, synchronisation, and updates. It separates client state (UI state) from server state (fetched data). useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) fetches data, caches it, and automatically re-fetches on window focus and mount. useMutation handles creating/updating/deleting data with optimistic updates and cache invalidation. It eliminates boilerplate for loading states, error handling, pagination, and background refetching. React Query is typically combined with Zustand or Context for UI state rather than Redux, which was historically used for both concerns.
24. What is Zustand?
Zustand is a minimal, fast state management library for React that uses a simple API without boilerplate. Create a store: const useStore = create((set) => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })) })). Consume in a component: const count = useStore(state => state.count). Components only re-render when their selected slice changes. Zustand supports async actions, middleware (devtools, persist, immer), and works outside React. It is increasingly preferred over Redux for medium-complexity applications due to its simplicity — no providers, no reducers, no action creators — while still providing predictable state updates.
25. What is the Context API and what are its limitations?
React Context provides a way to pass data through the component tree without prop drilling. It works well for infrequently changing data (theme, locale, current user). Limitations: every consumer re-renders whenever the context value changes — even if they only use a part of the context object. This can cause performance issues for frequently updating state. Solutions include splitting context into multiple smaller contexts, memoising the context value with useMemo, or using a state management library with selector-based subscriptions (Zustand, Redux Toolkit) where components only re-render when their specific slice changes.
Performance Optimisation
26. What is React.memo?
React.memo is a higher-order component that memoises a functional component, preventing re-renders when its props have not changed (shallow comparison). const MemoButton = React.memo(Button). If the parent re-renders but the button's props are identical, MemoButton skips re-rendering. Pass a custom comparison function as the second argument for deep comparison: React.memo(Component, (prevProps, nextProps) => prevProps.id === nextProps.id). React.memo is only beneficial when the component is expensive to render and receives stable props. Wrapping every component in React.memo adds overhead without benefit — use it selectively after profiling.
27. What causes unnecessary re-renders and how do you prevent them?
Unnecessary re-renders are caused by: parent component re-rendering (child always re-renders unless memoised), new object/array/function references created on each render (even if logically equal), and context value changes. Prevention strategies: React.memo to memoize components, useMemo for expensive computed values, useCallback for function props passed to memoised children, stable context values with useMemo, state colocation (keep state as close to where it is used as possible), and splitting large components into smaller ones so only the re-rendering part updates. Use React DevTools Profiler to identify which components re-render and why.
28. What is code splitting and lazy loading in React?
Code splitting divides the JavaScript bundle into smaller chunks loaded on demand, reducing initial load time. React.lazy(() => import('./HeavyComponent')) dynamically imports a component. It must be used with Suspense: <Suspense fallback={<Spinner />}><HeavyComponent /></Suspense>. React Router's route-based code splitting is the most impactful — each route loads only its own chunk. React.lazy works with default exports. For named exports, re-export as default or use a wrapper. Dynamic imports are resolved by bundlers (Webpack, Vite) which create separate chunks for each lazy-loaded module.
29. What is the React Profiler?
The React Profiler (in React DevTools) measures rendering performance of React component trees. It records what components rendered, why they rendered, and how long each render took. The flame chart shows the component tree per commit with colour intensity indicating render time. The ranked chart sorts components by render duration. The profiler highlights which components are rendering most frequently or taking the longest. Use it to identify bottlenecks before applying memoisation. The <Profiler> component in code provides programmatic profiling data: <Profiler id="Nav" onRender={callback}>.
30. What is React 18's Concurrent Mode?
Concurrent Mode (enabled by default in React 18 via createRoot) allows React to work on multiple UI versions simultaneously. React can interrupt, pause, and resume renders — it is no longer strictly synchronous. This enables: startTransition (mark non-urgent state updates), useDeferredValue (defer a value update to a lower priority), Suspense for data fetching (not just code splitting), automatic batching of all state updates (not just React event handlers), and streaming server rendering with renderToPipeableStream. Concurrent features make UIs feel more responsive by ensuring urgent updates (user input) are never blocked by non-urgent renders (data loading).
Advanced Patterns
31. What are higher-order components (HOCs)?
A higher-order component is a function that takes a component and returns a new enhanced component: const withAuth = (Component) => (props) => isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />. HOCs add cross-cutting concerns (authentication, logging, error boundaries, theming) without modifying the original component. They follow the same pattern as higher-order functions in functional programming. HOCs are a legacy pattern — custom hooks have largely replaced them for logic reuse in modern React. HOCs are still useful for class components and library code (Redux's connect, React Router's withRouter).
32. What is the render props pattern?
The render props pattern passes a function as a prop that the component calls to determine what to render. <DataFetcher url="/api/data" render={(data) => <Chart data={data} />} />. It allows sharing state logic between components without HOCs. The children prop as a function is a common variant: <Mouse>{({ x, y }) => <Cursor x={x} y={y} />}</Mouse>. Like HOCs, render props are largely superseded by custom hooks in modern React. They remain useful for components that expose render-time values (like react-window virtualised lists) and the Formik <Field render> pattern.
33. What are error boundaries?
Error boundaries are class components that catch JavaScript errors in their child component tree during rendering, in lifecycle methods, and in constructors, and display a fallback UI instead of crashing the whole app. They implement static getDerivedStateFromError() to render fallback UI and componentDidCatch() to log errors. Function components cannot be error boundaries — a class component wrapper (or the react-error-boundary library) is needed. Use cases: wrapping each route for per-page error isolation, wrapping third-party widgets, and catching errors in async rendering (with React 18). Error boundaries do not catch: event handler errors, async errors, server rendering errors, or errors inside the error boundary itself.
34. What is the Compound Component pattern?
The Compound Component pattern creates a group of components that share implicit state, working together to form a cohesive UI. Example: <Select> with <Select.Option> children — the Option components implicitly access the Select parent's selected state via Context. This gives consumers control over rendering without exposing internal state. Implementation: create a context in the parent, expose sub-components as static properties: Select.Option = Option. Compound components are used in headless UI libraries (Radix UI, Reach UI) and component libraries where consumers need rendering flexibility while the parent manages behaviour.
35. What are portals in React?
React Portals render children into a DOM node outside the parent component's DOM hierarchy. ReactDOM.createPortal(child, domNode). Common use case: modals, tooltips, and dropdowns that must escape CSS overflow: hidden or z-index constraints of their container. The portal's children are still part of the React component tree (events bubble through React's tree, not the DOM tree), so useContext and event propagation work normally. The portal target node (e.g., document.body) must exist in the DOM. Next.js provides a built-in <Portal> component. Portals solve the "stacking context" problem for overlays.
React Ecosystem
36. What is React Router?
React Router is the standard routing library for React applications. It provides declarative, component-based routing: <Routes><Route path="/" element={<Home />} /><Route path="/about" element={<About />} /></Routes>. In React Router v6: useNavigate() for programmatic navigation, useParams() for URL parameters, useSearchParams() for query strings, useLocation() for current location, and <Outlet> for nested layouts. Loaders and actions in v6.4+ (Data Router) handle data fetching and mutations at the route level, inspired by Remix. React Router integrates with browser history and enables client-side navigation without full page reloads.
37. What is Next.js and how does it extend React?
Next.js is a React framework that adds server-side rendering (SSR), static site generation (SSG), API routes, file-based routing, image optimisation, and more. It adds: getServerSideProps (SSR per request), getStaticProps/getStaticPaths (SSG at build time), pages/api/* for backend API routes, automatic code splitting per route, the <Image> component with automatic optimisation, and the App Router (Next.js 13+) with React Server Components, streaming, and layout nesting. Next.js solves React's lack of opinions on routing, data fetching, and deployment, making it the standard for production React applications.
38. What are React Server Components?
React Server Components (RSC) run on the server and never ship JavaScript to the client — they can directly access databases, file systems, and secrets without an API layer. RSC output is serialised and streamed to the client. Client components (marked with 'use client') handle interactivity and hooks. RSC reduces the client bundle size and eliminates client-side data fetching waterfalls. In Next.js App Router, all components are Server Components by default; only components with interactivity need 'use client'. RSC and Client Components compose: a Server Component can render Client Components but not vice-versa for server-only code.
39. What is Tailwind CSS and how is it used with React?
Tailwind CSS is a utility-first CSS framework that provides low-level utility classes (flex, mt-4, text-white, bg-blue-500) composed directly in JSX to build designs without writing custom CSS. Unlike component libraries (MUI, Ant Design), Tailwind provides building blocks with no pre-designed components — giving complete design control. In React: <button className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"> or with the cn() utility for conditional classes. Tailwind purges unused styles in production for minimal CSS bundle size. It integrates naturally with component-based architecture since styles live with components.
40. What is the difference between client-side and server-side rendering?
Client-side rendering (CSR): the server sends a minimal HTML shell; React renders the entire UI in the browser using JavaScript. Initial page load is slow (JS must download and execute before content appears), but subsequent navigation is fast. Poor SEO for content that search engines need to index. Server-side rendering (SSR): the server renders the full HTML for each request and sends it to the browser; React hydrates the HTML to add interactivity. Faster initial paint and better SEO but slower time-to-interactive if JavaScript is large. Static site generation (SSG) pre-renders HTML at build time — best performance for content that doesn't change per request.
Testing & Best Practices
41. How do you test React components?
React Testing Library (RTL) is the standard testing approach — it tests components from the user's perspective rather than implementation details. render(<Component />) renders into a virtual DOM. Queries: getByRole, getByText, getByLabelText find elements as users would. userEvent simulates realistic user interactions (typing, clicking, tabbing). expect(element).toBeInTheDocument() with jest-dom matchers. Key principle: test what the user sees and does, not component internals. Avoid testing implementation details (internal state, method calls). Snapshot tests capture component output — useful but can become maintenance burdens. Use MSW (Mock Service Worker) to mock API requests.
42. What is the difference between getBy, queryBy, and findBy in React Testing Library?
getBy* throws an error if no matching element is found — use for elements that must be present. queryBy* returns null if no matching element is found (no error) — use to assert an element is NOT present: expect(queryByText('Loading...')).not.toBeInTheDocument(). findBy* returns a Promise and waits for the element to appear (up to a default timeout) — use for elements that appear asynchronously after data fetching or animations. All three have AllBy variants (getAllBy, queryAllBy, findAllBy) that return arrays and match multiple elements.
43. What is hydration in React?
Hydration is the process of attaching React event handlers and interactivity to server-rendered HTML that has already been sent to the browser. The browser receives ready-to-display HTML (fast first paint), then React's JavaScript downloads and runs, attaching event listeners and state management to the existing DOM nodes (rather than replacing them). Hydration mismatches (differences between server-rendered HTML and what React expects to render) cause warnings and potential UI inconsistencies — common causes include rendering random values, using browser-only APIs during SSR, or conditional rendering based on client-only state. suppressHydrationWarning suppresses specific mismatch warnings.
44. What is the key prop and why is it important?
The key prop helps React identify which items in a list have changed, been added, or removed, enabling efficient reconciliation. When a list re-renders, React uses keys to match old and new elements. Without keys, React re-renders the entire list. With stable, unique keys (typically data IDs), React only updates changed items. Using array index as a key causes problems when the list order changes — React may reuse the wrong DOM element, causing state and animation bugs. Never use random values as keys (new keys on every render destroy and recreate components, losing state). Keys must be unique among siblings, not globally.
45. What is StrictMode in React?
React.StrictMode is a development-only wrapper that activates additional checks and warnings. In React 18, it double-invokes function component bodies, state initialisers, and useEffect setups and cleanups to help detect side effects that are not safe for concurrent rendering. It highlights: deprecated API usage, legacy lifecycle methods, unexpected side effects, missing useEffect cleanup, and unsafe state patterns. Strict Mode does not affect production — the double-invocation only happens in development. Wrapping your app with <StrictMode> during development is strongly recommended to catch bugs early, especially before adopting concurrent features.
46. What is reconciliation in React?
Reconciliation is the algorithm React uses to diff the virtual DOM tree from the previous and next renders and determine the minimum DOM updates required. Key heuristics: (1) Elements of different types (e.g., <div> to <span>) destroy the old tree and create a new one; (2) Elements of the same type update only the changed attributes; (3) Lists use key props for efficient matching. React processes the tree top-down. React Fiber (introduced in React 16) replaced the old synchronous reconciler with an asynchronous, interruptible architecture that enables concurrent rendering. Understanding reconciliation explains why key selection, element type consistency, and component structure affect performance.
47. What is forwardRef?
React.forwardRef allows a parent component to pass a ref to a child component's DOM node or internal ref. By default, refs cannot be attached to function components. forwardRef wraps the component and exposes the ref: const Input = React.forwardRef((props, ref) => <input ref={ref} {...props} />). The parent can then useRef to access the child's DOM node: inputRef.current.focus(). useImperativeHandle (used with forwardRef) lets you customise what is exposed via the ref — instead of exposing the raw DOM node, you expose specific methods. Necessary for building reusable input, modal, and animation components in component libraries.
48. What is the difference between useEffect and useLayoutEffect?
Both run after render, but at different times. useEffect runs asynchronously after the browser has painted the screen — it does not block the paint, making it the right choice for data fetching, subscriptions, and side effects that don't need to modify the DOM synchronously. useLayoutEffect runs synchronously after DOM mutations but before the browser paints — it blocks painting until it completes. Use useLayoutEffect when you need to read DOM layout (element dimensions, positions) and immediately make DOM changes based on those measurements to avoid flicker. In SSR contexts, useLayoutEffect causes warnings (not available on server); use useEffect or check typeof window !== 'undefined'.
49. What are the rules of hooks?
React hooks have two rules enforced by the eslint-plugin-react-hooks: (1) Only call hooks at the top level — never inside loops, conditions, or nested functions. This ensures React can maintain the correct order of hook calls between re-renders (hooks are tracked by their call order). (2) Only call hooks from React function components or custom hooks — not from regular JavaScript functions, class components, or event handlers. These rules exist because React identifies each hook call by its position in the component's execution order. Violating them causes incorrect state association and cryptic bugs that are hard to diagnose.
50. What is a headless UI component?
A headless component provides behaviour, accessibility, and state management without any styling, giving complete visual control to the consumer. Examples: Radix UI, Headless UI (by Tailwind Labs), React Aria (by Adobe). A headless Dialog component handles: focus trapping, Escape key dismissal, ARIA attributes (role="dialog", aria-modal), scroll locking, and animation state — but renders no visible styles. The consumer applies Tailwind, CSS modules, or any styling system. This separation of concerns enables highly accessible, fully customisable UI components without the constraint of pre-designed styles that are hard to override. Headless components are the foundation of modern design system toolkits.