React in TypeScript: A Quick Guide

React is amazing, but let's face it, as the application grows you can find yourself going back and forth, changing files, just because you don't remember how you called a prop. It just adds more chaos into the freedom that JavaScript offers, which often lead to bugs. That makes it imperative to use TypeScript. The adoption of static typing can help us catch errors in advance, prevent bugs, improve code maintainability and in the long term enhance productivity we provide to the developer the best experience.
I get it, it gets hard at first, that's why I created this quick cheatsheet, so you can catch up with the best static typing practices using React. It will provide you with a quick reference for commonly used patterns, syntax, and best practices when working with React and TypeScript.
Here's what's covered:
Props types
Children types
Event handlers
Lift prop events
Lift prop inputs
useState
useCallback and useMemo
useReducer
Prop types
Let's start with the one of the basics the props.
In the child we define what is going to be the type for the props
Use type instead of interface to make it reusable in other files.
export type CardProps = {
className: string;
};
const Card = (props: CardProps) => {
return <div className={props.className}>Card content</div>;
};
const Parent = () => {
return <Card className="class"></Card>;
};
Copied to clipboard!
Another version destructuring the props inline:
export type CardProps = {
className: string;
};
const Card = ({ className }: CardProps) => {
return <div className={className}>Card content</div>;
};
const Parent = () => {
return <Card className="class"></Card>;
};
Copied to clipboard!
And finally we can even define the types inline as well
const Card = ({ className }: {className: string}) => {
return <div className={className}>Card content</div>;
};
const Parent = () => {
return <Card className="class"></Card>;
};
Copied to clipboard!
Children types
Usually the easiest way to get this works (and tempting) is use any
in the children definition, but is just use the type React.ReactNode
And like before we can use the inline types as well if you prefer it.
export type CardProps = {
children: React.ReactNode;
};
const Card = (props: CardProps) => {
return <div>{props.children}</div>;
};
const Parent = () => {
return <Card>hi there</Card>;
};
Copied to clipboard!
Event handler types
Here we need to start using inference from TypeScript, usually this is going to be the types that we are going to use to define the props. Let's take a look to the following example.
export type CardProps = {
className: string;
children: React.ReactNode;
onClick: React.MouseEventHandler<HTMLButtonElement>;
};
const Card = ({ children, className, onClick }: CardProps) => {
return (
<button className={className} onClick={onClick}>
{children}
</button>
);
};
const Parent = () => {
const onClick = () => {
console.log('hi there')
}
return (
<Card className="my class" onClick={onClick}>
hi there
</Card>
);
};
Copied to clipboard!
We have a click event and If we hover over it we'll se a menu displaying the type, we can take it and insert in the definition of the props.

Lift events
What if I want to pass events? Let's look first at the definition of the props.
Now the props is going to become a function, this function will have an event as input and return a void function. We can type this event, otherwise It'll complain. To solve it just copy the last type definition and remove the Handler
because now we are defining the event and not the handler.
//step 1
export type CardProps = {
onClick: (event) => void
};
//step 2
export type CardProps = {
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void
};
Copied to clipboard!
Now in the parent the event type will be lifted automatically and we can use the hover to see the definition of it again, and type it in the function.

const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("on click", e);
};
Copied to clipboard!
Lift values
We might have the need to pass some value from the children to the parent as well. We can include it in the definition of the props. In the children we have to pass to the function the event and also the input, in this case and id
and define it in the prop definition. Finally we'll be able to see the type in the parent (hovering to the event) and include in our function.
export type CardProps = {
className: string;
children: React.ReactNode;
onClick: (event: React.MouseEvent<HTMLButtonElement>, id: number) => void;
};
const Card = ({ children, className, onClick }: CardProps) => {
return (
<div>
<button className={className} onClick={(event) => {onClick(event, 1)}}>
{children}
</button>
</div>
);
};
const Parent = () => {
const onClick = (e: React.MouseEvent<HTMLButtonElement>, id: number) => {
console.log("on click", e, id);
};
return (
<Card className="my class" onClick={onClick}>
hi there
</Card>
);
Copied to clipboard!
UseState
Looking at the following example we know for sure that the list
is going to be an array with objects, containing an id
and value
. What we can do is define the individual type and tell the state that list
is going to be an array of that type.
const Parent = () => {
const [list, setList] = useState([]);
return (
<div>
{list.map((element) => {
return <div key={element.id}>{element.value}</div>;
})}
</div>
);
};
Copied to clipboard!
export type List = {
id: number;
value: string;
};
const Parent = () => {
const [list, setList] = useState<List[]>([]);
return (
<div>
{list.map((element) => {
return <div key={element.id}>{element.value}</div>;
})}
</div>
);
};
Copied to clipboard!
UseCallback and UseMemo
These hooks are pretty simple, it's going to infer the type of the function inside, so what we actually have to do is type correctly the function inside and what is going to return. In the case of useMemo we can even type the returned value.
const onClick = useCallback(
(buttonName: string) => {
console.log(props.id, buttonName)
}
)
//option 1
const myList = useMemo(
() => {
return [1,2,3]
}
)
//option 2
const myList = useMemo<number[]>(
() => {
return [1,2,3]
}
)
Copied to clipboard!
UseReducer
It should be easy to type the state as number but the action it's a little tricky, we can have both add
and subtract
and place an option in the type is not going to be possible.
const reducer = (state: unknown, action: unknown) => {
switch (action.type) {
case "add":
return { count: state.count + action.add };
case "subtract":
return { count: state.count - action.subtract };
default:
throw new Error();
}
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: "add", add: 1 });
Copied to clipboard!
What it can be done is place optional type for the action reducer.
type ReducerState = {
count: number;
};
type ReducerAction =
| {
type: "add";
add: number;
}
| {
type: "subtract";
subtract: number;
};
const reducer = (state: ReducerState, action: ReducerAction) => {
switch (action.type) {
case "add":
return { count: state.count + action.add };
case "subtract":
return { count: state.count - action.subtract };
default:
throw new Error();
}
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: "add", add: 1 });
Copied to clipboard!
Resources
If you want to study more about how to correctly type your React code, you can also visit React Typescript Cheatsheet which I find quite useful.
Wrap up
Mastering TypeScript with React can be challenging sometimes, but once you've got the hang of it, it can significantly improve your development skills. It helps prevent bugs, enhances code maintainability, and boosts productivity.
In this guide, we've covered some commonly used patterns, syntax, and best practices with examples for prop types, children types, event handlers, lifted prop events and inputs, and the usage of several hooks such as useState, useCallback, useMemo, and useReducer. I hope you found this quick cheatsheet useful. As always, practice is key to mastering, so keep coding!
There's a lot more to explore, and I encourage you to dig deeper into the topic and learn more about the endless possibilities TypeScript offers when used in conjunction with React.
Happy coding! ✌️