Typing React Components in TypeScript (Practical Patterns)

Props, children, event handlers, generics in components, and common TS + React pitfalls interviewers ask about.

F

Frontend Interview Team

February 08, 2026

2 min read
Typing React Components in TypeScript (Practical Patterns)

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 e as any.
  • 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.FC everywhere (especially with generics)
  • Typing children incorrectly (or forgetting it)
  • Using any for events
  • Overusing as casts to silence errors
  • Mixing defaultProps patterns from old React

Interview questions to practice

  1. How do you type a component that wraps a <button>?
  2. How do you type children?
  3. When would you use forwardRef?
  4. 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.