How to Use PropTypes in React

In this article, you will learn what ''PropTypes'' means, how it works, and the various validators you can use to validate your props.

joel-olawanle-headshot-graphcms
Joel Olawanle
How to Use PropTypes in React

When working with React components, you sometimes need to pass props from a parent to a child component. When passing down these props, you may make mistakes and cause bugs that are hard to detect by passing in values as a wrong data type. For example, if you are expected to pass an age prop which should be a number, you mistakenly passed the number as a string.

import { Profile } from './Profile';
export const App = () => {
let user = { name: 'John Doe', age: '23' };
return <Profile userDetails={user} />;
};

If you want to maybe add to the age in the Profile component, it affects what will render on your React application. Also, unfortunately, React won’t throw an error; instead, it will render this as a concatenation.

export const Profile = ({ userDetails }) => {
return (
<p>
{userDetails.name} is {userDetails.age + 3}
years old
</p>
);
};

On the webpage, 233 years is rendered instead of 26 years because 23 was mistakenly passed in as a string. How about there is a way for you to check and validate each prop type that comes into a child component so it matches what you are expecting; else, it throws an error in your browser’s console.

By default, JavaScript does not have a built-in type-checking solution, so Microsoft introduced TypeScript. However, React has an internal method that you can use for props validation known as PropTypes.

In this article, you will learn what PropTypes is about, how it works, and when to use it. You will also learn the various types of PropTypes and, finally, see how it works with a real-world example that fetches content from Hygraph CMS.

What is React PropTypes?Anchor

As your React application grows, you’d become more intentional about the props you pass. You will want to ensure some props are required, set default values, and give props more validation power.

PropTypes serves as the method you can use to ensure the correct datatype is passed for each prop, meaning whatever is rendered is correct. It can also be used to ensure a value is passed, set default values, and lots more.

In plain terms, it is used as a validator to ensure the data your component receives is valid. There are many different types of PropTypes, and all have unique ES6 classes, which you can use.

Note: The significant difference between TypeScript and PropTypes is that propTypes is only checked in development mode while TypeScript performs these checks from your code environment.

If you don’t understand props well and what it means in React, let’s do a quick dive, so you can perfectly understand this article.

Understanding Props in ReactAnchor

A prop is a short form for “properties”. You can use it to pass down data from a parent component to a child component. For example, if you fetch a list of products from an external in the App.js component, you also want to use these same products in a child component. Instead of having to fetch or pass the data again, you can pass down the data easily.

import { Profile } from './Profile';
export const App = () => {
let name = "John Doe";
let age = 50;
return <Profile name={name} age={age} userName="Jonny" />;
};

This data can be received in the child component by destructuring or using the props parameter:

export const Profile = (props) => {
return (
<p>
{props.name} is {props.age + 3}
years old
</p>
);
};

Props can receive data in different datatypes such as arrays, objects, strings, numbers, and lots more.

How and when should you use PropTypesAnchor

When working with functions, you would pass in arguments, which, when these arguments are not passed, will throw an error or break your application. This also applies to React component; you will sometimes want to pass required prop(s) or validate the datatype of each prop because failure to pass in the correct prop will affect your application’s behavior. You can do all these with PropTypes.

Also, when working on large applications for clients and with a team, it is always good to validate these props to avoid unexpected production bugs.

How to use PropTypesAnchor

Previously, before the release of React v15.5, PropTypes was inbuilt into the React package, but now it has been moved into a different package which you will first have to install into your project to make use of it.

You can install it in your project by running the following command in your terminal:

$ npm install prop-types --save

Once it has been installed, you will first have to import it into whichever component you want to use to access its features.

import PropTypes from 'prop-types';

When you import it into your component, you can now attach the component name to the PropTypes method and create an object containing all your prop's validation.

import PropTypes from 'prop-types';
export const Profile = (props) => {
return (
<p>
{props.name} is {props.age + 3}
years old
</p>
);
};
Profile.propTypes = {
// Props validations here
};

Your PropTypes object will contain key-value pairs for each prop you validate, the key is the prop's name, and the value is the PropTypes validation (the type and other validation). Let's now explore the various Prop types with examples.

Types of PropTypesAnchor

The PropTypes method holds a wide range of classes that you can use to validate and configure type definitions. These types will be grouped into sections, so they are easy to understand.

Basic TypesAnchor

The basic types are the regular data types you always use in JavaScript. This includes string, number, object, array, boolean, function, and symbol. You can only use them by attaching the PropTypes method.

ComponentName.propTypes = {
anyValue: PropTypes.any, // the prop can be of any data type
myString: PropTypes.string, // prop should be a string
myNumber: PropTypes.number, // prop should be a number
myObject: PropTypes.object, // prop should be an object
myArray: PropTypes.array, // prop should be an array
myBoolean: PropTypes.bool, // prop should be boolean
myFunction: PropTypes.func, // prop should be a function
mySymbol: PropTypes.symbol, // prop should be a symbol
};

For example, if you pass three props which are name, age, and isLoggedIn, then you can configure their types:

import PropTypes from 'prop-types';
export const Profile = ({ name, age, isloggedIn }) => {
return (
<>
{isloggedIn && (
<p>
{name} is {age + 3}
years old
</p>
)}
</>
);
};
Profile.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
isloggedIn: PropTypes.bool,
};

You can make a prop required by attaching .isRequired, meaning when the prop is not passed, it will throw an error in our console.

Profile.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
isloggedIn: PropTypes.bool.isRequired,
};

When you don’t pass a particular prop, you will get an error like this in your console:

Multiple TypesAnchor

PropTypes can also allow you to set the type of values your props should be and the expected value. It allows you to set a limited set of values or multiple data types for a prop. This can be done using PropTypes.oneOf() and PropTypes.oneOfType() validators. These validators take in an array.

  • PropTypes.oneOf(): This validator can be used to set limited values for a prop. For example, if you want the value of your name prop to be either “John” or “Jane”. You can achieve this with this validator.
Profile.propTypes = {
name: PropTypes.oneOf(['John', 'Jane']).isRequired,
// ...
};

This means when you pass in another value asides from “John” and “Jane” to the name prop, it will throw an error:

  • PropTypes.oneOfType(): Just like you can set limited values for your prop, you can also set multiple datatypes for your prop, meaning if you want a prop value to either be an array or object, you can set it with this validator.
Profile.propTypes = {
// ...
arrayOrObject: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
};

Passing a value that may be a string to the arrayOrObject prop will throw an error to your console.

Collective TypesAnchor

You now know how these PropTypes work, and you have seen the basic types, similar to the regular data types in programming. Some other types help you validate more advanced types of props.

arrayAnchor

Asides from PropTypes.array validator, which you can use for array types, PropTypes uses the PropTypes.arrayOf() validator to perform more fine-tuned validation of arrays. This method allows you to check the type of values in an array.

For example, your component might expect a prop with an array of numbers, and then you can use PropTypes.arrayOf() to validate only numbers.

Profile.propTypes = {
// ...
myArray: PropTypes.arrayOf(PropTypes.number).isRequired,
};

If a typo error occurs, maybe a number is passed a string, then this validator will throw an error.

ObjectAnchor

Just as you have learned with Arrays, you can also use the PropTypes.objectOf() validator to check the type of values in an object.

Profile.propTypes = {
// ...
myObject: PropTypes.objectOf(PropTypes.string).isRequired,
};

In the code above, myObject prop will only accept an object with values that are string, such as:

myObject: {
pet: 'cat',
food: 'pizza',
},

When you maybe have a number as its value or introduce a new key-value pair such as age: 10, it will throw an error in your console.

Two other validators work with objects. These validators help you validate the type for each value in an object and can also help make the object closed, meaning when you add another key-value pair whose type is not declared it will throw an error. These two validators are:

  • PropTypes.shape(): This can be used to define a set of keys for an object and validate its data types. For example, if an object consists of name, age, and id, then you can validate the type for each key.
Profile.propTypes = {
userDetails: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
}),
};

This means that when you pass a string as the age value, it will throw an error. When you define all these validations for each key in an object, you will notice that when you define a new key-value pair, you can use it within your application without validating its type. You can fix this using the PropTypes.exact() validator.

  • PropTypes.exact(): This validator can be used for strict or exact object matching. This means that it will throw an error when you have extra properties in the object you are validating:
Profile.propTypes = {
userDetails: PropTypes.exact({
id: PropTypes.number,
name: PropTypes.string,
age: PropTypes.number,
}).isRequired,
};

Advanced Type checking for components and component namesAnchor

In some scenarios, you might have to pass in a component as a prop. To check if a prop is a component, you can use the PropTypes.element validator.

Component.propTypes = {
elementProp: PropTypes.element
}

One significant advantage and common use for the PropTypes.element validator is to ensure that a component has only one child. If the component has no children or multiple children, it will display a warning on the JavaScript console.

Component.propTypes = {
children: PropTypes.element.isRequired
}

Custom TypesAnchor

You have learned how to validate props with the default validators, but how about creating your validators. Suppose you want to validate the value of a prop to be sure it matches what you expect— maybe an email value.

You can do this by creating a custom validator. To create a custom validator, you will create a function that can take in three arguments which are:

  • props: The original object containing all the props passed to the component
  • propName: The name of the prop you want to validate
  • componentName: The name of the component

In the function, you can maybe use a regex to test if the value is an actual email, check for the length, and lots more, and then throw an error if the validation fails using new Error().

Profile.propTypes = {
userDetails: PropTypes.shape({
// ...
email: function (props, propName, componentName) {
const regex = /^[\w-.][email protected]([\w-]+\.)+[\w-]{2,4}$/g;
if (!regex.test(props[propName])) {
return new Error(
`The prop ${propName} passed to ${componentName}is not a valid email address.`
);
}
},
}),
};

When you pass in a wrong email value, it will throw the error. You can also decide to extract the validator into a function, so your propTypes object is neat and easy to maintain.

const isEmail = function (props, propName, componentName) {
const regex = /^[\w-.][email protected]([\w-]+\.)+[\w-]{2,4}$/g;
if (!regex.test(props[propName])) {
return new Error(
`The prop ${propName} passed to ${componentName}is not a valid email address.`
);
}
};
Profile.propTypes = {
userDetails: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
age: PropTypes.number,
email: isEmail,
}),
};

Setting the default Prop valueAnchor

So far, you have learned how to validate props, configure PropTypes, and lots more. It is also important to know that you can set a default value for each prop. You can do this similarly to how PropTypes work but this time, you would use defaultProps (there is no need to import it).

Profile.defaultProps = {
userDetails: {
id: 1,
name: 'John Doe',
age: 30,
},
};
Note: This will only work when you fail to pass in the userDetails prop to the component.

React PropTypes ExampleAnchor

Let’s now use PropTypes validators in a real-life project to see how it works. Here is the source code for the project (a cocktail app that fetches content from Hygraph). On the Products.jsx page, which lists all available cocktails, the array is looped, and each object is passed as a prop to the Product.Jsx component.

On the Product.jsx component, the object is validated with the PropTypes.object.

import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
const Product = ({ product }) => {
return (
<div key={product.id} className="cocktail-card">
<img src={product.image.url} alt="" className="cocktail-img" />
<div className="cocktail-info">
<div className="content-text">
<h2 className="cocktail-name">{product.name}</h2>
<span className="info">{product.info}</span>
</div>
<Link to={`/products/${product.slug}`}>
<div className="btn">View Details</div>
</Link>
</div>
</div>
)
}
Product.propTypes = {
product: PropTypes.object.isRequired
}
export default Product

Also, on the ProductDetails.jsx page, you will notice an object is passed into the CocktailInfo.jsx component, where each object value is validated using PropTypes.shape().

import PropTypes from 'prop-types';
const CocktailInfo = ({ productInfo }) => {
return (
<div className="cocktail-infos">
<div className="row">
<h3 className="label">Name: </h3>
<p className="text">{productInfo.name}</p>
</div>
<div className="row">
<h3 className="label">Category: </h3>
<p className="text">{productInfo.category}</p>
</div>
<div className="row">
<h3 className="label">Info: </h3>
<p className="text">{productInfo.info}</p>
</div>
<div className="row">
<h3 className="label">Instructions: </h3>
<p className="text">{productInfo.instructions}</p>
</div>
<div className="row">
<h3 className="label">Ingredients: </h3>
<p className="text">{productInfo.ingredients}</p>
</div>
</div>
)
}
CocktailInfo.propTypes = {
productInfo: PropTypes.shape({
name: PropTypes.string,
category: PropTypes.string,
info: PropTypes.string,
ingredients: PropTypes.string,
instructions: PropTypes.string
})
}
export default CocktailInfo

You can check the GitHub repository to cross-check the code and the deployed project here.

ConclusionAnchor

In this article, you have learned what PropTypes means, how it works, and the various validators you can use to validate your props.

Have fun coding!