Q211: Typing React Props, Children and Component APIs
What Interviewers Want To Evaluate
Interviewers want to know whether you can type React components in a way that keeps UI APIs clear, safe, and pleasant to use.
They check prop modeling, children, component composition, discriminated props, polymorphic components, event handlers, refs, and when to avoid overly clever generic components.
Short Interview Answer
I type React props by modeling the component's public contract first. Required props should represent what the component needs to render correctly, optional props should have clear defaults, and mutually exclusive variants should use discriminated unions. I type children intentionally instead of always using React.FC, and I keep component APIs narrow enough that callers cannot create invalid UI combinations.
Detailed Interview Answer
React component types are product API types.
A component's props decide:
what callers must provide
what states are possible
what variants exist
what events are exposed
what composition is allowed
what invalid combinations are blocked
Good TypeScript makes the component easier to use correctly.
Basic Props
Start with explicit props.
type ButtonProps = {
label: string;
disabled?: boolean;
onClick: () => void;
};
function Button({ label, disabled = false, onClick }: ButtonProps) {
return (
<button disabled={disabled} onClick={onClick}>
{label}
</button>
);
}
The defaults are implemented in code.
The optional marker only says the caller may omit the prop.
Typing children
Use React.ReactNode when a component can render normal React children.
type PanelProps = {
title: string;
children: React.ReactNode;
};
function Panel({ title, children }: PanelProps) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
}
Do not add children by habit.
Some components should not accept children.
type IconButtonProps = {
ariaLabel: string;
icon: React.ReactNode;
onClick: () => void;
};
This keeps the API precise.
React.FC Trade-Off
React.FC can work, but I usually prefer plain functions with explicit props.
Reasons:
children should be intentional
return type inference is usually fine
generic components can be awkward with React.FC
plain functions are simpler to read
This is a style choice, but the contract should be clear either way.
Event Handlers
Use React event types when exposing DOM events.
type SearchInputProps = {
value: string;
onChange: (value: string) => void;
};
function SearchInput({ value, onChange }: SearchInputProps) {
return (
<input
value={value}
onChange={(event) => onChange(event.currentTarget.value)}
/>
);
}
This API hides DOM event details from callers.
If callers need the event, expose it intentionally.
type RawInputProps = {
onChange: React.ChangeEventHandler<HTMLInputElement>;
};
Discriminated Props
Use unions for mutually exclusive variants.
type LinkButtonProps = {
kind: "link";
href: string;
children: React.ReactNode;
};
type ActionButtonProps = {
kind: "button";
onClick: () => void;
children: React.ReactNode;
};
type SmartButtonProps = LinkButtonProps | ActionButtonProps;
Render by discriminant.
function SmartButton(props: SmartButtonProps) {
if (props.kind === "link") {
return <a href={props.href}>{props.children}</a>;
}
return <button onClick={props.onClick}>{props.children}</button>;
}
Now callers cannot pass both href and onClick accidentally.
Avoid Boolean Explosion
This is weak:
type AlertProps = {
success?: boolean;
warning?: boolean;
error?: boolean;
};
It allows multiple variants at once.
Prefer:
type AlertProps = {
tone: "success" | "warning" | "error" | "info";
children: React.ReactNode;
};
Single-source variant props are easier to reason about.
Component Composition
Some APIs should accept render functions.
type DataState<TData> =
| { status: "loading" }
| { status: "error"; message: string }
| { status: "success"; data: TData };
type DataPanelProps<TData> = {
state: DataState<TData>;
children: (data: TData) => React.ReactNode;
};
This preserves data type for the render function.
function DataPanel<TData>({ state, children }: DataPanelProps<TData>) {
if (state.status === "loading") return <p>Loading</p>;
if (state.status === "error") return <p>{state.message}</p>;
return <>{children(state.data)}</>;
}
Forwarding Refs
Refs need careful typing.
const TextInput = React.forwardRef<HTMLInputElement, { label: string }>(
function TextInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
},
);
Use refs for imperative access when needed, not as a default data flow.
Native Element Props
You can extend native props.
type NativeButtonProps = React.ComponentPropsWithoutRef<"button">;
type AppButtonProps = NativeButtonProps & {
tone?: "primary" | "secondary";
};
Be careful not to expose too much.
If the design system needs a controlled API, omit or wrap confusing native props.
Common Mistakes
Common mistakes include:
adding children to every component
using many booleans for variants
exposing DOM events when callers only need values
making generic components before reuse is real
using any for render props
forgetting accessibility props in icon-only components
The component API should guide correct usage.
Senior Trade-Offs
I optimize component props for:
clear call sites
invalid states blocked by types
accessible defaults
minimal public surface
stable variant names
reasonable inference
If types become harder than the component, I simplify the API.
Interview Framing
In an interview, say:
I treat React props as a public API. I model variants with unions, children intentionally, events at the right abstraction level, and generics only when they preserve caller data.
Revision Notes
- Type props as a component contract.
childrenshould be intentional.- Discriminated unions block invalid prop combinations.
- Value callbacks are often cleaner than raw DOM events.
- Native prop extension is useful but can expose too much.
- Generic components should preserve real caller information.
Follow-Up Questions
- Why avoid adding
childreneverywhere? - When would you use discriminated props?
- How do you type a render prop?
- What is the trade-off with extending native button props?
- How do refs affect component API design?
How I Would Answer This In A Real Interview
I would explain that strong React prop typing is about API design. I use explicit props, intentional children, and discriminated unions for mutually exclusive variants. I prefer callbacks that expose the domain value when callers do not need DOM events. For reusable components, I use generics only when they keep caller data typed through render functions or callbacks.