Composition, prop drilling, children, higher order components, render props and the patterns interviewers use to test how you structure a component tree.
13 questions5 fresher, 6 mid-level, 2 senior
Once the definitions are out of the way, interviewers move to structure: how do you split a screen into components, how does data reach the component that needs it, and how do you reuse behaviour without copying code. These questions separate someone who has finished a tutorial from someone who has maintained a component tree that other people also work in.
Expect at least one of these to turn into a whiteboard question, usually "how would you build a reusable X". Answer with composition first and abstraction second.
1.What is component composition, and why does React prefer it to inheritance?
Mid-level
Composition means building a component by passing other components into it, usually through children or named props, instead of extending a base component. React recommends it because a component's output is data, so passing that data around is more flexible than inheriting behaviour.
A generic layout that knows nothing about what it contains.
With inheritance a specialised card has to know about its base class and the base class has to anticipate its subclasses. With composition Card only knows it receives some nodes and renders them, so it can hold anything, including components written after it.
Likely follow-up questions
When would you use a higher order component instead of composition?
How do you share behaviour rather than markup?
2.What is prop drilling and how do you avoid it?
Mid-level
Prop drilling is passing a prop through components that do not use it, just to reach a descendant that does. You avoid it with component composition, with context, or with a state library, in that order.
The cost of drilling is not performance, it is coupling. Every intermediate component now has the prop in its signature, so it has to be updated when the shape changes and it cannot be reused without it.
The fixes, cheapest first
Composition. Instead of passing user down three levels, render the leaf where the data already is and pass the finished element down as children. This solves a surprising share of real cases and adds nothing.
Context. Good for values that are genuinely global to a subtree: theme, locale, the signed in user, a design system's configuration.
A state library. Redux Toolkit, Zustand or Jotai, when the state is large, shared widely, and updated from many places.
A server state library. If the value came from an API, TanStack Query removes the drilling by making the data available wherever it is queried.
Likely follow-up questions
Is context a state management solution?
How would you stop every consumer re-rendering when one field of a context value changes?
3.What is the children prop?
Fresher
children is the special prop that holds whatever was written between a component's opening and closing tags. It is what makes wrapper components such as layouts, cards and modals possible.
function Panel({ children }) {
return <div className='panel'>{children}</div>;
}
<Panel>
<h2>Title</h2>
<p>Body</p>
</Panel>
// children is an array of two elements
children can be an element, an array of elements, a string, a number, null, or even a function if the component expects one.
Passing a function as children is the render props pattern.
Do not assume children is an array. With one child it is that child, not an array of one, which is why React.Children exists.
Likely follow-up questions
What does React.Children.map do that children.map does not?
4.How do you give a prop a default value in a function component?
Fresher
Use a JavaScript default in the destructuring: function Button({ variant = 'primary' }). The legacy Component.defaultProps object still works for classes but is deprecated for function components.
function Button({ variant = 'primary', size = 'md', children }) {
return <button className={`btn-${variant} btn-${size}`}>{children}</button>;
}
Likely follow-up questions
What happened to defaultProps in React 19?
5.What is a higher order component?
Mid-level
A higher order component is a function that takes a component and returns a new component wrapping it with extra behaviour. It was the pre-hooks way of sharing non visual logic between components.
Do not mutate the component you are given. Return a new one.
Pass unrelated props straight through with {...props}.
Set a displayName on the wrapper or React DevTools shows an anonymous tree.
Never create an HOC inside render. A new component type every render means React unmounts and remounts the whole subtree.
Most HOCs are better written as a custom hook today: hooks share logic without adding wrapper components, so you avoid the nesting and the prop collisions. HOCs are still the right tool when the shared thing has to render something around the component, for example an error boundary or a permission gate.
A render prop is a prop whose value is a function returning JSX. The component owns some behaviour or state and hands it to the caller, who decides what to draw with it.
Render props solved the same problem as HOCs without the wrapper component, and with an explicit contract you can read at the call site. Hooks made most of these obsolete too, but the pattern survives in libraries whose behaviour is tied to what they render, such as virtualised lists and drag and drop.
Likely follow-up questions
Why did hooks largely replace render props?
7.What are refs, and what does forwardRef do?
Mid-level
A ref is an escape hatch to a DOM node or a mutable value that survives renders without causing one. forwardRef lets a parent attach a ref to a DOM node inside a child component, because refs are not passed through as ordinary props.
8.What are the ways to do conditional rendering in React?
Fresher
An if statement before the return, a ternary inside JSX, logical AND for the render-or-nothing case, or returning null to render nothing at all.
if (isLoading) return <Spinner />; // early return
{isAdmin ? <AdminPanel /> : <UserPanel />} // ternary
{error && <ErrorMessage error={error} />} // logical AND
return null; // render nothing
Returning null still mounts the component and still runs its lifecycle, it simply produces no DOM. That is different from not rendering the component at all.
Likely follow-up questions
What renders and what does not when a child is false, null, undefined or 0?
If a component returns null, do its effects still run?
9.How do you render a list, and what should you watch out for?
Fresher
Map the array to elements and give each one a stable key. Watch for index keys, for mutating the source array, and for doing filtering or sorting work on every render.
sort and reverse mutate in place. Copy first with [...todos].sort(...) or you will mutate props.
Keys must be unique among siblings, not globally.
For thousands of rows, virtualise with a library rather than rendering everything.
Likely follow-up questions
How would you render a list of 10,000 rows?
10.What is the container and presentational component pattern, and is it still relevant?
Mid-level
It splits components into containers that fetch data and hold state, and presentational components that only take props and render. Hooks made the strict split much less necessary, but the underlying idea of keeping rendering separate from data access is still good practice.
The pattern existed because before hooks the only way to attach data to a component was a class with lifecycle methods, so you wrapped a pure renderer in a stateful class. A custom hook now does that job without a second component.
What survives is the principle. A component that both queries an API and renders a complex layout is hard to test and impossible to reuse with different data. Pull the data access into a hook, keep the component taking plain props, and you get the same benefit without the extra layer.
Likely follow-up questions
How do Server Components change this split?
11.What is the compound component pattern?
Senior
Compound components are a set of components that share implicit state through context and are composed by the consumer, like <Tabs>, <Tabs.List> and <Tabs.Panel>. It gives users layout freedom without a wall of configuration props.
The alternative is a single <Tabs items={[...]} renderPanel={...} /> with a prop for every decision. That is easier to start with and worse to live with: every new layout requirement becomes another prop. Compound components push layout back to the consumer and keep only the shared state inside.
Likely follow-up questions
How would you make this work when a consumer renders a tab inside a wrapper div?
How do you keep it accessible?
12.How do you validate props — PropTypes or TypeScript?
Fresher
TypeScript, for anything new. It checks at compile time across the whole codebase, while PropTypes only warns in the console at runtime in development, and PropTypes was removed from React in version 19.
PropTypes still has a niche: validating data that arrives at runtime from somewhere the type system cannot see, such as an untyped API response. Even there, a runtime schema validator like Zod is the better tool, because it can parse and narrow rather than just warn.
Likely follow-up questions
How would you type a component that accepts any valid button attribute?
13.Why would you use React.Children instead of mapping over children directly?
Senior
Because children is not guaranteed to be an array. React.Children.map handles a single child, nested arrays, null and fragments, and it also prefixes keys so nesting does not produce key collisions.
// Breaks when there is exactly one child
{children.map((child) => cloneElement(child, { size }))}
// Safe
{React.Children.map(children, (child) =>
isValidElement(child) ? cloneElement(child, { size }) : child
)}