[Ctrl J]
[Ctrl K]
light/dark
React and TypeScript are a powerful combination for building robust web applications. In this blog post, we'll explore how to use TypeScript with React, focusing on defining and using prop types. We'll cover everything from basic prop types to more advanced concepts like children props, union types, and function props.
Getting Started with Basic Props Types
Let's start with a simple example of how to define prop types for a React component using Typescript.
type GreetProps = {name: string;};const Greet = (props: GreetProps) => {return <h1>Hello, {props.name}!</h1>;};
In this example, we've defined a GreetProps
type with a single property name
of type string
. We then use this type to specify the props for our Greet
component.
// ...type GreetProps = {name: string;};const Greet = (props: GreetProps) => {return <h1>Hello, {props.name}!</h1>;};const page = () => {return (<div><Greet name="John" /></div>);};// ...
Expanding Prop Types
Now, let's expand our GreetProps
to include other basic prop types:
// ...type GreetProps = {name: string;age: number;isStudent: boolean;hobbies: string[];address: {street: string;city: string;};};const Greet = (props: GreetProps) => {return (<div><h1>Hello, {props.name}!</h1><p>Age: {props.age}</p><p>Student: {props.isStudent ? "Yes" : "No"}</p><p>Hobbies: {props.hobbies.join(", ")}</p><p>Address: {props.address.street}, {props.address.city}</p></div>);};// ...
In this expanded example, we've added:
age
as a number
isStudent
as a boolean
hobbies
as an array of stringsaddress
as an object with street
and city
propertiesDestructuring Props
To make our component code cleaner, we can destructure the props directly in the function parameters:
//...const Greet = ({ name, age, isStudent, hobbies, address }: GreetProps) => {return (<div><h1>Hello, {name}!</h1><p>Age: {age}</p><p>Student: {isStudent ? "Yes" : "No"}</p><p>Hobbies: {hobbies.join(", ")}</p><p>Address: {address.street}, {address.city}</p></div>);};// ...
This approach can make your code more readable, especially when you're using many props.
Optional Props
In many cases, you might want to make some props optional. Optional means that a prop can be provided or omitted without causing an error. You can do this by adding a ?
after the prop name.
type GreetProps = {name: string;age?: number;isStudent?: boolean;hobbies?: string[];address?: {street: string;city: string;};};const Greet = ({ name, age, isStudent, hobbies, address }: GreetProps) => {return (<div><h1>Hello, {name}!</h1>{age && <p>Age: {age}</p>}{isStudent !== undefined && <p>Student: {isStudent ? "Yes" : "No"}</p>}{hobbies && <p>Hobbies: {hobbies.join(", ")}</p>}{address && (<p>Address: {address.street}, {address.city}</p>)}</div>);};
In this example, all props except name
are optional. We've also added some conditional rendering to handle cases where optional props are not provided.
Somewhat more advanced prop types
Now let's explore some more advanced prop types that you'll often encounter in React applications.
Function Props
Function props are commonly used for callbacks and event handlers.
"use client";type GreetProps = {name: string;onGreet: (name: string) => void;};const Greet = ({ name, onGreet }: GreetProps) => {return <button onClick={() => onGreet(name)}>Greet {name}</button>;};// usageconst page = () => {const handleGreet = (name: string) => {alert(`Hello, ${name}!`);};return <Greet name="Alice" onGreet={handleGreet} />;};export default page;
In this example, onGreet
is typed as a function that takes a string
parameter and returns void
. This clearly defines the expected shape of the function prop.
Children
In React, the children
prop is a special prop that allows you to pass components as data to other components. Here's how you can type it:
type ContainerProps = {children: React.ReactNode;};const Container = ({ children }: ContainerProps) => {return <div>{children}</div>;};// usageconst page = () => {return (<div><Container><h1>Hello, World!</h1><p>This is a child element.</p></Container></div>);};export default page;
In this example, React.ReactNode
is used to type the children
prop. It's a catch-all type for anything that can be rendered in React: JSX, strings, numbers, fragments, arrays, etc.
Union of String Literals
Union types are powerful in TypeScript, allowing you to specify that a prop can be one of several specific values. This is particularly useful for status props or any prop with a fixed set of possible values:
type StatusProps = {status: "loading" | "success" | "error";};const StatusMessage: React.FC<StatusProps> = ({ status }) => {switch (status) {case "loading":return <p>Loading...</p>;case "success":return <p>Data loaded successfully!</p>;case "error":return <p>Error loading data. Please try again.</p>;}};// usageconst page = () => {return (<div><StatusMessage status="loading" />{/* Later, when status changes: */}<StatusMessage status="success" /></div>);};export default page;
This approach ensures that status
can only be one of the specified values, providing type safety and autocompletion in your IDE.
Conclusion
TypeScript's type system allows for precise and flexible typing of React props. By using a range of prop types from basic to advanced, you can create robust and self-documenting React components. These techniques not only catch errors early but also improve the developer experience through better autocompletion and type inference.
Remember to experiment with these concepts and adapt them to your specific use cases. As you become more comfortable with these patterns, you'll find yourself writing more reliable and maintainable React applications.
Thank you for reading and happy coding!