Typing React Components in TypeScript (Practical Patterns)
Props, children, event handlers, generics in components, and common TS + React pitfalls interviewers ask about.
Frontend Interview Team
February 08, 2026
What you’ll learn
- The most useful React + TypeScript patterns interviewers expect
- How to type props, children, events, and refs cleanly
- When to use generics in components
- Common TS + React mistakes (and how to avoid them)
30‑second interview answer
In React + TypeScript, you typically type components by defining a Props type and annotating the function parameter. Use ReactNode for children, ComponentProps<'button'> to inherit native props, and explicit event types like React.ChangeEvent<HTMLInputElement>. Avoid React.FC by default (it can hide issues with children and generics). Use generics when the component’s behavior depends on the type (e.g., Select<T>).
1) The default pattern: Props + function
type Props = {
title: string;
subtitle?: string;
};
export function Hero({ title, subtitle }: Props) {
return (
<header>
<h1>{title}</h1>
{subtitle ? <p>{subtitle}</p> : null}
</header>
);
}Why this is preferred:
- clear
- supports generics later
- no magic defaults
2) Typing children
Use React.ReactNode:
import type { ReactNode } from 'react';
type Props = {
children: ReactNode;
};
export function Card({ children }: Props) {
return <div className="card">{children}</div>;
}Avoid children?: any.
3) Extending native element props (best DX)
This is one of the best “senior” patterns:
import type { ComponentPropsWithoutRef } from 'react';
type ButtonProps = ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
};
export function Button({ variant = 'primary', className, ...rest }: ButtonProps) {
return <button className={`${variant} ${className ?? ''}`} {...rest} />;
}Why it’s good:
- You automatically get
onClick,disabled,type, etc. - Works nicely with design systems.
4) Event handlers
Common ones:
function onInputChange(e: React.ChangeEvent<HTMLInputElement>) {
console.log(e.target.value);
}
function onFormSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
}
function onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === 'Enter') {
// ...
}
}Interview pitfall:
- Don’t type
easany. - Prefer the specific event type so you get correct
target/currentTarget.
5) Typing refs + forwardRef
import { forwardRef } from 'react';
import type { ComponentPropsWithoutRef } from 'react';
type InputProps = ComponentPropsWithoutRef<'input'> & {
label: string;
};
export const LabeledInput = forwardRef<HTMLInputElement, InputProps>(
function LabeledInput({ label, ...props }, ref) {
return (
<label>
<span>{label}</span>
<input ref={ref} {...props} />
</label>
);
}
);6) Generics in components (when you really need them)
Example: a select that returns a typed value.
type Option<T> = { label: string; value: T };
type SelectProps<T> = {
options: Option<T>[];
value: T;
onChange: (v: T) => void;
};
export function Select<T>({ options, value, onChange }: SelectProps<T>) {
return (
<select
value={String(value)}
onChange={(e) => {
const selected = options.find((o) => String(o.value) === e.target.value);
if (selected) onChange(selected.value);
}}
>
{options.map((o) => (
<option key={String(o.value)} value={String(o.value)}>
{o.label}
</option>
))}
</select>
);
}Tradeoff:
- Generic components can be tricky with JSX inference.
- Use them when they materially improve safety.
7) Common mistakes
- Using
React.FCeverywhere (especially with generics) - Typing
childrenincorrectly (or forgetting it) - Using
anyfor events - Overusing
ascasts to silence errors - Mixing
defaultPropspatterns from old React
Interview questions to practice
- How do you type a component that wraps a
<button>? - How do you type
children? - When would you use
forwardRef? - When is a generic component worth it?
Quick recap
- Prefer
Props+ function components. - Use
ComponentPropsWithoutRef<'tag'>to inherit native props. - Use correct React event types.
- Use generics when behavior depends on the type.