Prop Validation with PropTypes in React
PropTypes in React
Prop Validation with PropTypes is essential for catching bugs early. As your React application grows, catching bugs early becomes critical. One of the simplest yet most powerful tools for preventing prop-related bugs is PropTypes, React’s built‑in runtime type checking system.
What are PropTypes in React?
PropTypes are only checked in development mode, they add zero overhead to your production build.
Installation
npm install prop-types
Then import it into your component file:
import PropTypes from 'prop-types';
FAQs:
Define a .propTypes object on your component function or class. Use .isRequired for mandatory props and defaultProps for fallbacks.
import PropTypes from 'prop-types';
function ProductCard({ name, price, inStock, rating }) {
return (
<div>
<h3>{name}</h3>
<p>Price: ${price.toFixed(2)}</p>
<p>Rating: {rating}/5</p>
<p>{inStock ? 'In Stock' : 'Out of Stock'}</p>
</div>
);
}
ProductCard.propTypes = {
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
inStock: PropTypes.bool,
rating: PropTypes.number,
};
ProductCard.defaultProps = {
inStock: true,
rating: 0,
};
| Validator | What It Checks | Example |
|---|---|---|
PropTypes.string |
Must be a string | .isRequired |
PropTypes.number |
Must be a number | PropTypes.number |
PropTypes.bool |
Must be a boolean | .isRequired |
PropTypes.array |
Must be an array | PropTypes.array |
PropTypes.object |
Must be an object | PropTypes.object |
PropTypes.func |
Must be a function | .isRequired |
PropTypes.node |
Anything renderable (string, JSX, number) | PropTypes.node |
PropTypes.element |
A React element | PropTypes.element |
PropTypes.oneOf([...]) |
One of a list of values | .oneOf(['sm','md','lg']) |
PropTypes.arrayOf(Type) |
Array of a given type | .arrayOf(PropTypes.string) |
PropTypes.shape({...}) |
Object with specific shape | .shape({ id: PropTypes.number }) |
Use PropTypes.shape and PropTypes.arrayOf to validate nested structures.
import PropTypes from 'prop-types';
function OrderSummary({ order }) {
return (
<div>
<p>Order #{order.id}</p>
<ul>
{order.items.map((item) => (
<li key={item.id}>{item.name} x {item.qty}</li>
))}
</ul>
<p>Total: ${order.total.toFixed(2)}</p>
</div>
);
}
OrderSummary.propTypes = {
order: PropTypes.shape({
id: PropTypes.number.isRequired,
total: PropTypes.number.isRequired,
items: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
qty: PropTypes.number.isRequired,
})
).isRequired,
}).isRequired,
};
Yes,use PropTypes.oneOf.
Button.propTypes = {
size: PropTypes.oneOf(['small', 'medium', 'large']),
variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),
};
Use PropTypes.element (exactly one React element) or PropTypes.node (any renderable content).
function Card({ icon, children }) {
return <div>{icon}{children}</div>;
}
Card.propTypes = {
icon: PropTypes.element, // e.g., <StarIcon />
children: PropTypes.node, // any renderable content
};
Not really, but they can still be useful as runtime checks. TypeScript gives you compile‑time safety, while PropTypes add runtime validation in development. Many teams use one or the other, TypeScript is now the industry standard for new projects.
Putting It All Together
Real‑world example using arrayOf, shape, oneOf, and element:
import PropTypes from 'prop-types';
function FAQItem({ item, onToggle, isOpen }) {
return (
<div className="faq-item">
<button onClick={() => onToggle(item.id)}>
{item.question}
</button>
{isOpen && <p>{item.answer}</p>}
</div>
);
}
FAQItem.propTypes = {
item: PropTypes.shape({
id: PropTypes.number.isRequired,
question: PropTypes.string.isRequired,
answer: PropTypes.string.isRequired,
}).isRequired,
onToggle: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
};
function FAQList({ title, faqs, theme, headerElement, onToggle }) {
return (
<div className={`faq-list theme-${theme}`}>
{headerElement}
<h2>{title}</h2>
{faqs.map((faq) => (
<FAQItem
key={faq.id}
item={faq}
onToggle={onToggle}
isOpen={faq.isOpen}
/>
))}
</div>
);
}
FAQList.propTypes = {
title: PropTypes.string.isRequired,
faqs: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
question: PropTypes.string.isRequired,
answer: PropTypes.string.isRequired,
isOpen: PropTypes.bool,
})
).isRequired,
theme: PropTypes.oneOf(['light', 'dark', 'auto']),
headerElement: PropTypes.element,
onToggle: PropTypes.func.isRequired,
};
FAQList.defaultProps = {
theme: 'light',
headerElement: null,
};
Best Practices:
- Always define
propTypesfor reusable components, it acts as documentation. - Use
defaultPropsfor optional props, makes your component more predictable. - Prefer
isRequiredfor props that are truly mandatory, fewer bugs. - Combine shape, arrayOf, and oneOf to accurately model complex data.
- Don’t over‑validate, simple components don’t need every prop checked.
- Upgrade to TypeScript for larger projects, it gives you compile‑time guarantees and better tooling.
