React 19 Interview Questions
Short answer
React 19 shipped Actions and the form hooks, the `use` hook, ref as an ordinary prop, native document metadata, and stable Server Components. The theme connecting them is moving work off the client: less JavaScript shipped, and less manual plumbing around async state.
| Feature | Replaces | The point |
|---|---|---|
| Server Components | Client-only components | Their JavaScript never reaches the browser |
| Actions + useActionState | Manual loading/error state | Async transitions handled for you |
| useFormStatus | Prop-drilling a pending flag | A child reads its form's state directly |
| useOptimistic | Hand-rolled optimistic updates | Automatic rollback on failure |
| use() | Conditional hook workarounds | Read a promise or context conditionally |
| ref as a prop | forwardRef | One less wrapper |
| Document metadata | react-helmet | <title> and <meta> hoist natively |
| React Compiler | Manual useMemo / useCallback | Memoisation inserted at build time |
What actually changed in React 19?
The headline items are Actions with their supporting hooks, the `use` hook, ref becoming an ordinary prop, native support for document metadata, and Server Components reaching stability in the frameworks that implement them. React Compiler is adjacent — a separate build-time tool rather than part of the runtime — but it comes up in the same conversations because it changes how much memoisation you write by hand.
The connecting theme is worth stating explicitly, because it is what an interviewer is checking you understand. React 19 is mostly about doing less on the client: shipping less JavaScript through Server Components, and writing less async plumbing through Actions. Nearly every individual feature is an instance of one of those two ideas, and framing your answer that way is far stronger than listing the features in the release notes order.
What are React Server Components, and how do they differ from SSR?
They solve different problems and are frequently confused. Server-side rendering runs your components on the server to produce HTML, then ships the same components to the browser to hydrate them — the JavaScript is sent either way. A Server Component runs only on the server, and its code is never sent at all; what crosses the network is the rendered output. SSR improves the time to first paint; Server Components reduce the bundle.
1// app/products/page.tsx — a Server Component (no "use client")
2import AddToCart from "./add-to-cart";
3
4export default async function Products() {
5 const items = await db.product.findMany(); // runs on the server
6 return items.map(p => (
7 <article key={p.id}>
8 <h2>{p.name}</h2>
9 <AddToCart id={p.id} /> {/* the only client JavaScript */}
10 </article>
11 ));
12}
13
14// → the database code, the ORM and this component's own code
15// never reach the browser. Only add-to-cart.js is hydrated.The rules that follow are the practical half of the answer. A Server Component cannot use state, effects, refs, event handlers, or browser APIs, because none of those exist where it runs. It can be `async` and await data directly, which removes the effect-plus-loading-state dance entirely. A Client Component, marked with `"use client"`, is the opposite — and everything it imports becomes client code too, which is why the boundary should sit as low in the tree as possible.
What are Actions and useActionState?
An Action is an async function passed to a form's `action` prop or run inside a transition. React handles the pending state, errors and sequencing around it, which removes the three pieces of state almost every submit handler used to declare by hand. `useActionState` wraps that up: you give it a function and an initial state, and it returns the current state, a wrapped action to pass to the form, and a pending flag.
1// Before: three states, managed manually
2const [loading, setLoading] = useState(false);
3const [error, setError] = useState(null);
4async function onSubmit(e) {
5 e.preventDefault();
6 setLoading(true); setError(null);
7 try { await save(new FormData(e.target)); }
8 catch (err) { setError(err); }
9 finally { setLoading(false); }
10}
11
12// After: React owns the transition
13function Form() {
14 const [state, action, pending] = useActionState(
15 async (prev, formData) => {
16 try { await save(formData); return { ok: true }; }
17 catch (e) { return { error: e.message }; }
18 },
19 { ok: false }
20 );
21
22 return (
23 <form action={action}>
24 <input name="email" />
25 <button disabled={pending}>{pending ? "Saving…" : "Save"}</button>
26 {/* → React owns pending; no useState for loading or error */}
27 {state.error && <p role="alert">{state.error}</p>}
28 </form>
29 );
30}Two supporting hooks complete the picture. `useFormStatus` lets a component inside a form read that form's pending state without being passed a prop, which is what makes a reusable submit button possible. `useOptimistic` shows a provisional value while the action runs and rolls it back automatically if the action fails — the same optimistic-update pattern people used to hand-roll, with the rollback path included rather than forgotten.
One consequence of Actions worth calling out is that they work without JavaScript. Because an Action is attached to a real form element rather than to an onSubmit handler, a form can submit and be processed before hydration completes — which is progressive enhancement arriving in React by default rather than as something you build deliberately. That is a strong point to raise if the discussion turns to accessibility or slow devices.
What is the `use` hook?
`use` reads a resource — a promise or a context — and, unlike every other hook, it can be called conditionally and inside loops. When given a promise it suspends the component until the promise resolves, which lets a Client Component consume data streamed from a Server Component without an effect. It is not a data-fetching library and does not deduplicate or cache, so it is meant to consume promises created elsewhere, not to start requests during render.
1function Comments({ promise, show }) {
2 if (!show) return null;
3
4 const comments = use(promise); // legal — `use` breaks the hook rules
5 return comments.map(c => <p key={c.id}>{c.text}</p>);
6}
7
8// The promise is created on the SERVER and passed down:
9// <Suspense fallback={<Spinner />}>
10// <Comments promise={getComments()} show />
11// </Suspense>
12// → the server streams the result in when it resolves
13
14const theme = use(ThemeContext); // also reads context, conditionallyThe restriction that makes `use` safe is that it must still be called during render, inside a component or another hook — it is not a general-purpose await. It also pairs with Suspense rather than replacing it: the promise suspends, and the nearest Suspense boundary above renders its fallback. Explaining that pairing is usually the follow-up, because it is what makes streaming from the server actually work.
This is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399Why is forwardRef no longer needed?
In React 19 `ref` is an ordinary prop on function components, so you can accept it in the props object and spread it like anything else. `forwardRef` still works and is not removed, but it is no longer required, which deletes a wrapper from a great deal of component-library code. Ref callbacks can now also return a cleanup function, which is a small but genuinely useful addition.
1// React 18
2const Input = forwardRef(function Input(props, ref) {
3 return <input ref={ref} {...props} />;
4});
5
6// React 19 — ref is just a prop
7function Input({ ref, ...props }) {
8 return <input ref={ref} {...props} />;
9}
10
11// Ref callbacks can clean up after themselves now
12<div ref={(node) => {
13 const ro = new ResizeObserver(onResize);
14 ro.observe(node);
15 return () => ro.disconnect(); // → called on unmount
16}} />The ref-as-a-prop change has a knock-on effect worth knowing for library work. Because ref is now an ordinary prop, it flows through spreads and wrapper components without special handling, so a design-system component that forwards `{...props}` gets ref forwarding for free. That removes one of the more tedious parts of building a component library, where almost every primitive previously needed a forwardRef wrapper.
What does React Compiler change?
React Compiler analyses your components at build time and inserts memoisation automatically, which removes most of the reason to write `useMemo`, `useCallback` and `React.memo` by hand. It is a separate opt-in build tool rather than part of React itself, and it bails out on components it cannot prove are safe — which is why the rules of React, particularly not mutating props or state, matter more once it is enabled.
The interview trap here is treating the compiler as a reason not to understand memoisation. It memoises for exactly the reasons you would have: keeping a reference stable so a child can skip re-rendering, and avoiding recomputation of an expensive value. When it bails out on a component you still have to read the warning and know what it was trying to do — so the mechanism remains worth knowing even in a codebase where you never write the hooks yourself.
A fair question to expect back is whether the compiler makes performance work unnecessary, and the honest answer is no. It removes the memoisation boilerplate, but it does nothing about an oversized bundle, an unvirtualised list of ten thousand rows, a waterfall of sequential requests, or a layout that thrashes on every scroll. Those are architectural problems, and they are what performance rounds actually ask about.
What else is worth knowing for a React 19 question?
A handful of smaller changes come up as follow-ups. Document metadata now hoists natively, so a `<title>` or `<meta>` rendered anywhere in the tree moves into the head without a helmet library. Stylesheets and async scripts can be rendered inline with precedence handling. The `ref` cleanup mentioned above, better hydration error messages that show a diff, and Context being usable directly as `<Context>` rather than `<Context.Provider>` round out the list.
1function ProductPage({ product }) {
2 return (
3 <article>
4 {/* hoisted into <head> automatically */}
5 <title>{product.name} — ForgeFrontend</title>
6 <meta name="description" content={product.summary} />
7 <link rel="canonical" href={product.url} />
8 <h1>{product.name}</h1>
9 </article>
10 );
11}
12
13// Provider is now optional on the context itself
14<ThemeContext value={theme}>{children}</ThemeContext>
15// → same as <ThemeContext.Provider value={theme}>Worth being honest about scope, too: if the role uses plain React without a framework, Server Components and Actions may not apply, because they need a bundler and router integration that React alone does not provide. Saying "these need a framework like Next.js — in a Vite SPA the relevant changes are ref-as-a-prop, the metadata hoisting and the compiler" is a more accurate answer than reciting the whole list regardless of context.
1// A reusable submit button that knows nothing about its form
2function SubmitButton() {
3 const { pending } = useFormStatus();
4 return <button disabled={pending}>{pending ? "Saving…" : "Save"}</button>;
5 // → reads the ENCLOSING form's state; no prop needed
6}
7
8// Optimistic UI with the rollback included
9function Likes({ count, likeAction }) {
10 const [optimistic, addOptimistic] = useOptimistic(
11 count,
12 (current, delta) => current + delta
13 );
14
15 return (
16 <form action={async () => { addOptimistic(1); await likeAction(); }}>
17 <SubmitButton />
18 <span>{optimistic}</span>
19 {/* → increments instantly; reverts by itself if likeAction throws */}
20 </form>
21 );
22}How do you migrate from React 18?
For most applications the upgrade is undramatic. The removals are the parts to check: `propTypes` and `defaultProps` are gone for function components, string refs and legacy context are removed, and `ReactDOM.render` and `unmountComponentAtNode` are gone in favour of `createRoot`. The official codemods handle the mechanical part, so the real work is usually running the app and reading the warnings.
- →Replace defaultProps on function components with default parameter values.
- →Move propTypes to TypeScript, or accept losing the runtime checks.
- →Swap ReactDOM.render for createRoot if you have not already.
- →Replace string refs and legacy context — both are fully removed.
- →Check libraries for peer-dependency support before upgrading, which is usually the real blocker.
- →Adopt Actions and Server Components incrementally; nothing forces you to.
Do Server Components replace SSR?
No — they are a component model, not a rendering strategy, and they work alongside server rendering. SSR decides where HTML is produced; Server Components decide which code reaches the browser at all. A page can be server-rendered and still ship a large bundle if everything in it is a Client Component.
Can you use hooks in a Server Component?
No. useState, useEffect, useRef and event handlers all require a client runtime that does not exist on the server. A Server Component can be async and await data directly, which covers most of what an effect used to do for data fetching.
Is useActionState the same as useFormState?
It is the renamed version. useFormState was the canary name in React 18 experiments; useActionState is the stable API in React 19, and it additionally returns a pending flag. Older tutorials use the previous name, which is a common source of confusion.
Does React Compiler make useMemo obsolete?
In a codebase where it is enabled and not bailing out, largely yes for the routine cases. You still need to understand what it does, because you have to read its warnings, work in projects that do not use it, and answer interview questions about the mechanism.
Is forwardRef deprecated?
It still works and has not been removed, but it is no longer necessary — ref is an ordinary prop on function components now. New code should accept ref in props; existing forwardRef components can be migrated whenever convenient rather than urgently.
Frequently asked questions
- What are the main new features in React 19?
- Actions with useActionState, useFormStatus and useOptimistic; the use hook for reading promises and context; ref as an ordinary prop instead of forwardRef; native document metadata hoisting; and stable Server Components. React Compiler is a separate build-time tool that arrived alongside it.
- What is the difference between Server Components and SSR?
- SSR renders your components on the server to produce HTML but still ships those components to the browser for hydration. A Server Component runs only on the server and its JavaScript is never sent — only the rendered output crosses the network. One improves first paint; the other reduces bundle size.
- What can't you do in a Server Component?
- Use state, effects, refs, event handlers, or any browser API, because none of them exist where it runs. In exchange it can be async and await data directly, which removes the effect-plus-loading-state pattern that client-side data fetching required.
- What is the use hook in React 19?
- A way to read a resource — a promise or a context — that, uniquely among hooks, can be called conditionally and inside loops. Given a promise it suspends the component until it resolves. It does not cache or deduplicate, so it consumes promises created elsewhere rather than starting requests during render.
- What are Actions in React 19?
- Async functions passed to a form's action prop or run inside a transition, with React managing the pending state, errors and sequencing. useActionState wraps a function and returns the current state, a wrapped action for the form, and a pending flag — replacing the loading and error state most submit handlers declared by hand.
- What is useOptimistic used for?
- Showing a provisional value while an action is in flight and rolling it back automatically if it fails. It makes likes, follows and toggles feel instant. The automatic rollback is the important part, because hand-rolled optimistic updates routinely omit the error path and leave the UI showing something untrue.
- Is forwardRef removed in React 19?
- No, it still works. It is simply no longer needed, because ref is passed as an ordinary prop to function components. Migrate existing components when convenient; there is no urgency and no deprecation warning forcing the change.
- What does React Compiler do?
- It analyses components at build time and inserts memoisation automatically, removing most manual useMemo, useCallback and React.memo. It is opt-in and separate from React itself, and it bails out on code it cannot prove safe — which makes following the rules of React, especially not mutating props or state, more important.
- Do I need Next.js to use React 19 features?
- For Server Components and Actions, effectively yes — they require bundler and router integration that React alone does not provide, so a framework like Next.js implements them. Ref-as-a-prop, metadata hoisting, the use hook and the compiler all work in a plain React app.
- What was removed in React 19?
- propTypes and defaultProps for function components, string refs, legacy context, and the old ReactDOM.render and unmountComponentAtNode APIs. Codemods handle most of the mechanical migration; library peer-dependency support is usually the real blocker.
- How is useActionState different from useFormState?
- It is the same hook renamed for the stable release, with a pending flag added to what it returns. useFormState was the experimental name in React 18 canaries, so older articles use it — a frequent cause of confusion when following tutorials.
- Should I upgrade to React 19?
- For most applications yes, once your dependencies declare support. The breaking changes are narrow and codemodded, and the new APIs are additive — you can adopt Actions and Server Components gradually or not at all while still benefiting from ref-as-a-prop and the metadata handling.
This is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399