ReactValidating Props with PropTypes

Validating Props with PropTypes

When building components that will be used by other developers (or by your future self), it is helpful to declare exactly which props a component expects and what type each one should be. The prop-types package provides runtime type-checking for React props in development. When a wrong type is passed, PropTypes prints a descriptive warning to the browser console.

Installing prop-types

Bash
npm install prop-types
Defining PropTypes on a Component

After defining your component, attach a propTypes object to it. Each key is a prop name; the value is a validator from the PropTypes object:

JSX
import PropTypes from 'prop-types'

function UserCard({ name, age, email, isPremium, onFollow }) {
  return (
    <div className="card">
      <h2>{name}</h2>
      <p>{age} years old</p>
      <p>{email}</p>
      {isPremium && <span className="badge">Premium</span>}
      <button onClick={onFollow}>Follow</button>
    </div>
  )
}

UserCard.propTypes = {
  name:      PropTypes.string.isRequired,
  age:       PropTypes.number.isRequired,
  email:     PropTypes.string.isRequired,
  isPremium: PropTypes.bool,
  onFollow:  PropTypes.func.isRequired,
}

export default UserCard
Note
PropTypes checks run **only in development mode**. They are stripped from production builds automatically by bundlers like Vite and webpack. There is no runtime cost in production.
Built-in Validators
  • PropTypes.string — the prop must be a string

  • PropTypes.number — the prop must be a number

  • PropTypes.bool — the prop must be a boolean

  • PropTypes.func — the prop must be a function

  • PropTypes.array — the prop must be an array (any array)

  • PropTypes.object — the prop must be an object

  • PropTypes.node — anything renderable: string, number, element, array, fragment

  • PropTypes.element — a single React element (&lt;Component /&gt;)

  • PropTypes.symbol — a JavaScript Symbol

  • PropTypes.any — any value (use sparingly)

isRequired

Append .isRequired to any validator to make the prop mandatory. If the prop is missing or undefined, PropTypes logs a warning:

JSX
UserCard.propTypes = {
  name:  PropTypes.string.isRequired,   // warning if missing
  email: PropTypes.string.isRequired,   // warning if missing
  age:   PropTypes.number,              // optional — no warning if undefined
}
PropTypes.shape — Validating Object Structure

When a prop is an object with a specific shape, use PropTypes.shape() to validate each key independently:

JSX
import PropTypes from 'prop-types'

function ProductCard({ product, onAddToCart }) {
  return (
    <div className="product-card">
      <img src={product.imageUrl} alt={product.name} />
      <h3>{product.name}</h3>
      <p>${product.price.toFixed(2)}</p>
      <p>{product.inStock ? 'In Stock' : 'Out of Stock'}</p>
      <button onClick={() => onAddToCart(product.id)} disabled={!product.inStock}>
        Add to Cart
      </button>
    </div>
  )
}

ProductCard.propTypes = {
  product: PropTypes.shape({
    id:       PropTypes.number.isRequired,
    name:     PropTypes.string.isRequired,
    price:    PropTypes.number.isRequired,
    imageUrl: PropTypes.string.isRequired,
    inStock:  PropTypes.bool.isRequired,
  }).isRequired,
  onAddToCart: PropTypes.func.isRequired,
}
PropTypes.arrayOf — Typed Arrays

JSX
// An array of strings:
tags: PropTypes.arrayOf(PropTypes.string)

// An array of shaped objects:
ProductList.propTypes = {
  products: PropTypes.arrayOf(
    PropTypes.shape({
      id:    PropTypes.number.isRequired,
      name:  PropTypes.string.isRequired,
      price: PropTypes.number.isRequired,
    })
  ).isRequired,
}
PropTypes.oneOf — Enum Values

JSX
// Restrict to a specific set of allowed values:
Button.propTypes = {
  variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),
  size:    PropTypes.oneOf(['sm', 'md', 'lg']),
}

// oneOfType — accept multiple types:
Badge.propTypes = {
  label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
}
Custom Validators

For complex rules, write a custom validator — a function that returns an Error on failure and null on success:

JSX
Avatar.propTypes = {
  // Must be a positive number between 16 and 512
  size: (props, propName, componentName) => {
    const val = props[propName]
    if (typeof val !== 'number') {
      return new Error(
        `${componentName}: '${propName}' must be a number, got ${typeof val}`
      )
    }
    if (val < 16 || val > 512) {
      return new Error(
        `${componentName}: '${propName}' must be between 16 and 512, got ${val}`
      )
    }
    return null
  },
}
Pairing with defaultProps

You can pair PropTypes with defaultProps to document both the expected type and the fallback value in one place (though ES6 default parameters are preferred in modern code):

JSX
function Button({ label, variant, size }) {
  return <button className={`btn btn--${variant} btn--${size}`}>{label}</button>
}

Button.propTypes = {
  label:   PropTypes.string.isRequired,
  variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),
  size:    PropTypes.oneOf(['sm', 'md', 'lg']),
}

// Legacy defaultProps — prefer ES6 defaults in the function signature
Button.defaultProps = {
  variant: 'primary',
  size: 'md',
}
Warning
`defaultProps` on function components is deprecated as of React 19. Use ES6 default parameter values in the function signature instead: `function Button({ label, variant = 'primary', size = 'md' })`.
PropTypes vs TypeScript

PropTypes check types at runtime (in the browser console during development). TypeScript checks types at compile time (in your editor and CI). For new projects TypeScript is the strongly recommended approach — it catches errors before the code even runs, enables autocomplete, and requires no extra runtime dependency.

If your project already uses TypeScript, you generally do not need PropTypes — the TypeScript compiler provides the same guarantees (and more) without the runtime overhead:

TSX
// TypeScript alternative — no prop-types package needed
interface UserCardProps {
  name:      string
  age:       number
  email:     string
  isPremium?: boolean
  onFollow:  () => void
}

function UserCard({ name, age, email, isPremium = false, onFollow }: UserCardProps) {
  return (
    <div>
      <h2>{name}</h2>
      <p>{age} · {email}</p>
      {isPremium && <span>Premium</span>}
      <button onClick={onFollow}>Follow</button>
    </div>
  )
}
Tip
Use PropTypes for JavaScript projects (no TypeScript). Use TypeScript interfaces/types for TypeScript projects. You rarely need both — the TypeScript compiler covers everything PropTypes does at the type level.