Skip to content
React rc components 4 min read

Validating Props

Props are the contract between a component and whoever renders it. Validating that contract catches mistakes early, documents what a component expects, and powers editor autocomplete. In modern React the default approach is TypeScript, which checks your props at compile time and gives you instant feedback in the editor. For plain JavaScript projects the legacy prop-types package provides runtime checks instead. This page covers both, and when each makes sense.

Typing props with TypeScript

The recommended way to validate props in 2025 is to describe them with a TypeScript interface (or type) and annotate the component’s parameter. TypeScript then enforces the shape at compile time, so a missing or mistyped prop is a build error rather than a runtime surprise.

interface GreetingProps {
  name: string;
  age: number;
  isAdmin: boolean;
}

function Greeting({ name, age, isAdmin }: GreetingProps) {
  return (
    <p>
      {name} ({age}) {isAdmin ? "— administrator" : ""}
    </p>
  );
}

export default Greeting;

Rendering <Greeting name="Ada" age={36} isAdmin /> type-checks. Forgetting age, or passing age="36" as a string, produces an error in your editor and breaks tsc before the code ever runs.

Prefer interface for public component props — it gives cleaner error messages and supports declaration merging. Use type when you need unions, intersections, or mapped types.

Required vs optional props

By default every property in an interface is required. Mark a prop optional by appending ? to its name. Optional props arrive as undefined when the parent omits them, so handle that case in your logic.

interface ButtonProps {
  label: string;        // required
  variant?: "solid" | "ghost"; // optional, union-typed
  disabled?: boolean;   // optional
}

function Button({ label, variant = "solid", disabled = false }: ButtonProps) {
  return (
    <button className={`btn btn-${variant}`} disabled={disabled}>
      {label}
    </button>
  );
}

Default values

Defaults belong in the destructuring pattern, not in a separate defaultProps object (which is deprecated for function components in React 19). Notice how variant and disabled above fall back to sensible values while remaining optional in the type.

Typing children and events

React ships ready-made types for common cases. Use React.ReactNode for renderable children and the synthetic event types for handlers.

interface CardProps {
  title: string;
  children: React.ReactNode;
  onSelect?: (id: string) => void;
}

function Card({ title, children, onSelect }: CardProps) {
  return (
    <section onClick={() => onSelect?.(title)}>
      <h2>{title}</h2>
      {children}
    </section>
  );
}

Legacy runtime validation with prop-types

Before TypeScript became standard, the prop-types package validated props at runtime. It still works and is useful in plain JavaScript codebases where a compile step isn’t available. Install it first:

npm install prop-types

Then attach a propTypes object to the component. React checks the values during development and logs a console warning on a mismatch.

import PropTypes from "prop-types";

function Greeting({ name, age, isAdmin }) {
  return (
    <p>
      {name} ({age}) {isAdmin ? "— administrator" : ""}
    </p>
  );
}

Greeting.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired,
  isAdmin: PropTypes.bool,
};

export default Greeting;

If you render <Greeting name="Ada" age="36" />, the validator fires a warning:

Output:

Warning: Failed prop type: Invalid prop `age` of type `string`
supplied to `Greeting`, expected `number`.

The chained .isRequired marks a prop as mandatory; omitting it makes the prop optional. There are validators for every primitive plus arrayOf, shape, oneOf, oneOfType, and func.

TypeScript vs prop-types

AspectTypeScriptprop-types
When it checksCompile timeRuntime (dev only)
Editor autocompleteYesNo
Build dependencyNeeds tsc/Vite TSPlain JS, no build step
Stripped in productionN/A (types erased)Should be stripped manually
StatusRecommended defaultLegacy / maintenance
CatchesMost type errors before runErrors only when code executes

The two are not usually combined: if you already use TypeScript, prop-types is redundant noise. Reach for prop-types only in JavaScript projects, or temporarily while migrating a codebase to TypeScript.

prop-types warnings are silenced in production builds and add bundle weight. TypeScript types vanish entirely at build time, costing zero runtime bytes — another reason it’s the modern choice.

Best Practices

  • Use TypeScript interfaces as the default way to validate props in new projects.
  • Make props required by default; mark something optional only when the component genuinely works without it.
  • Provide defaults in the destructuring pattern instead of the deprecated defaultProps.
  • Use precise types — string-literal unions like "solid" | "ghost" beat a bare string.
  • Type children with React.ReactNode and handlers with React’s synthetic event types.
  • Reserve prop-types for plain JavaScript codebases, and remember its checks run only in development.
  • Don’t pair prop-types with TypeScript — pick one validation strategy per component.
Last updated June 14, 2026
Was this helpful?