Website review
This review may include AI-generated content and is not individually verified. Domain, certificate and other facts come from the observed data on this page.
What is React TypeScript Cheatsheet?
React TypeScript Cheatsheet is a free, community-maintained reference site for developers who write React components in TypeScript. Rather than teaching React or TypeScript from scratch, it answers the recurring "how do I type this?" questions you hit mid-project: props, hooks, refs, events, context, and common component patterns.
The site is organized into three rough layers:
- Introduction — setup guidance for React with TypeScript across frameworks such as Next.js, Remix, Gatsby, and Expo.
- Topic pages — typing component props, hooks, class components,
defaultProps, forms and events, context, refs, portals, error boundaries, concurrent rendering, and reusable patterns. - API reference — focused notes on the
@types/reacttypes you reach for most, includingComponentProps,CSSProperties,ReactNode, andRef.
Who it suits. It fits developers who already know React and are adding or deepening TypeScript, and teams standardizing how they type shared components. It is less useful as a first tutorial; if you have never written either technology, learn the basics first and keep this open as a lookup.
How it compares to alternatives. The official React docs cover typing at a conceptual level but stay framework-agnostic and relatively brief. React is the better starting point for how React works. TypeScript explains the type system itself, which matters when an error is about generics or narrowing rather than React. Total TypeScript leans toward paid, exercise-driven learning. This cheatsheet sits in the middle: quick, example-first answers without a course structure.
A concrete scenario. You are building a reusable <Select> and need to type a generic options prop plus the onChange handler. The Introduction and API reference sections are the places to look for ComponentProps and pattern examples, then adapt them to your own prop names.
Decision criterion. Use it when you want a copy-adaptable snippet in seconds. When you need to understand why a type behaves as it does, follow the pattern back to the underlying @types/react definition or the TypeScript handbook.
Next step. Open the popular-topics list, find the entry closest to your current blocker — hooks, forms and events, or forwardRef — and read that page before searching elsewhere.
How do I type React hooks like useState and useEffect in TypeScript?
Type React hooks by giving the hook an explicit type argument or by letting TypeScript infer it from the initial value, then typing the callback parameters in useEffect or handling the undefined case when a ref or DOM node may not exist yet.
useState
- Let inference do the work when the initial value already describes the state:
const [count, setCount] = useState(0)gives younumber. - Reach for an explicit argument when the initial value is narrower than the state you want:
useState<User | null>(null)lets you start empty and assign a user later. - If a value can be absent, include
undefinedornullin the type rather than relying on a cast. That forces you to handle the empty case at the point of use. - For arrays that start empty, type the element:
useState<Item[]>([]), otherwise TypeScript may infernever[]and reject later pushes.
useEffect
- The callback takes no arguments for a plain effect. Type the values you read inside it, not the callback itself.
- The return value is either nothing or a cleanup function:
return () => { ... }. TypeScript checks this shape for you. - When you touch the DOM, narrow the ref first:
if (!node) return;before usingnode.scrollHeightor similar. This is the most common source of errors for people new to typed React. - Dependencies are inferred from what you reference. If you extract a function used in the effect, wrap it in
useCallbackso its identity is stable and the dependency list stays honest.
A quick comparison
| Hook | When inference is enough | When to annotate |
|---|---|---|
useState |
Initial value matches the final type | State can be null/undefined, or starts as an empty array |
useEffect |
Almost always | Rarely; type the ref or the values you read |
useRef |
Ref holds a simple mutable value | Ref points at a DOM element: useRef<HTMLDivElement>(null) |
A concrete scenario
You are building a search box that fetches results. You start with useState<Result[]>([]) so the list type is fixed before any data arrives, and a useState<string>('') for the query. In useEffect, you read the query, call the API, and return a cleanup that aborts the request. The abort controller lives in a useRef<AbortController | null>(null) so it survives re-renders without triggering them. Each of these annotations exists to close a specific gap that inference alone leaves open.
For a focused reference on the @types/react types you will reach for most, including ComponentProps, ReactNode and Ref, see React TypeScript Cheatsheet. If you want the official wording on hook rules and types, the React docs at React cover the runtime behaviour that these types describe.
How do I type function component props and defaultProps in TypeScript?
Type function component props by declaring an explicit props type and using it in the component signature. For defaults, prefer default parameter values in the function signature; defaultProps is legacy and behaves differently with TypeScript, especially for function components, so it is usually better to avoid it.
Function component props
Use a type or interface for the props object, then annotate the parameter. This gives you autocomplete, required/optional checking, and errors for unknown props.
type ButtonProps = {
label: string;
onClick?: () => void;
disabled?: boolean;
};
function Button({ label, onClick, disabled = false }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
Key points:
label: stringis required; omitting it is a type error.onClick?: () => voidis optional and may beundefined.- Destructuring with
disabled = falsegives a default value inside the component. - Do not type props as
any; that removes the main benefit of using TypeScript with React.
Default values
The simplest and most type-safe approach is default parameter values:
type CardProps = {
title: string;
subtitle?: string;
elevated?: boolean;
};
function Card({ title, subtitle = "No subtitle", elevated = false }: CardProps) {
// subtitle is string, elevated is boolean
}
This keeps the call site clean and makes the default visible where the prop is used. The external prop type still allows subtitle to be omitted, because it is optional.
Why defaultProps is awkward
With function components, defaultProps is a legacy pattern. TypeScript often cannot infer that a prop with a default is non-optional inside the component, so you may still need to handle undefined or use extra type helpers. It also interacts poorly with newer React versions and function component types. For new code, use default parameters instead.
If you must use defaultProps, keep it on the component and be prepared to widen or narrow the props type carefully. The React TypeScript Cheatsheet covers defaultProps alongside typing component props, hooks, forms and events, context, refs, and reusable patterns, so it is a useful reference when you need the exact edge-case behavior.
Choosing between them
| Approach | Best for | Trade-off |
|---|---|---|
| Default parameter values | Most function components | Defaults live in the function body; clear and type-safe |
defaultProps |
Legacy class-style or older codebases | Easier to misuse with TypeScript; can require extra typing |
| Optional props without defaults | Props that may genuinely be absent | You must handle undefined inside the component |
Practical next step
Start with a type for props and default parameter values. If you are unsure how to type a more complex case, such as forwardRef, generic components, or event handlers, check the relevant sections in the React TypeScript Cheatsheet and compare its examples with the React types in your project.
How do I handle forms and events with TypeScript in React?
Typing forms and events in React comes down to three things: type the event handler's parameter, type the form's state, and let @types/react infer what it can. The React TypeScript Cheatsheet covers forms and events as a dedicated topic, alongside hooks, context, and refs.
The core pattern
For a controlled input, the handler receives a change event. The reliable approach is to type the parameter explicitly rather than relying on inference from an inline arrow:
const [email, setEmail] = useState("");
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setEmail(e.target.value);
}
<input value={email} onChange={handleChange} />
ChangeEvent<HTMLInputElement> gives you e.target.value as a string. Swap the generic for HTMLTextAreaElement or HTMLSelectElement as needed.
Typing the submit handler
Use React.FormEvent<HTMLFormElement> and call preventDefault():
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
// validate and send
}
Event types worth knowing
| Event | Type |
|---|---|
| Input/select/textarea change | ChangeEvent<HTMLInputElement> (or the matching element) |
| Form submit | FormEvent<HTMLFormElement> |
| Button click | MouseEvent<HTMLButtonElement> |
| Key press | KeyboardEvent<HTMLInputElement> |
| Focus/blur | FocusEvent<HTMLInputElement> |
Practical trade-offs
- Inline handlers:
onChange={(e) => setEmail(e.target.value)}infers the type correctly, so you often need no annotation. Extracted named functions need the explicit type, which is why the cheatsheet-style pattern above annotates. - One state object vs. many: a single
useState({ email: "", name: "" })keeps the submit logic tidy but you must spread the previous state on every update. SeparateuseStatecalls are simpler to type and update. - Uncontrolled forms: reading values via
FormDataon submit avoids per-keystroke state entirely, at the cost of losing live validation. Type the entries withString(formData.get("email"))sincegetreturnsFormDataEntryValue | null.
Next step
If you're starting fresh, decide first whether the form is controlled or uncontrolled; that choice determines how much event typing you'll write. Then check the cheatsheet's forms and events section for the specific element types you're using, and its "useful patterns by use case" section if you need a reusable typed field component.
How do I use forwardRef and refs with TypeScript in React?
Use forwardRef when a parent needs to pass a ref down to a DOM node or component inside a child function component. In TypeScript, the key is to give the ref a precise element type and let forwardRef infer the props.
import { forwardRef, useRef, useImperativeHandle } from "react";
type InputProps = { label: string };
const TextInput = forwardRef<HTMLInputElement, InputProps>(
({ label }, ref) => (
<label>
{label}
<input ref={ref} />
</label>
)
);
function Form() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<TextInput label="Email" ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus</button>
</>
);
}
The first type argument is the element or handle the ref points to; the second is the component's own props. That ordering is the most common source of confusion.
Common patterns
- DOM refs:
forwardRef<HTMLInputElement, Props>when the ref lands on an<input>. Swap inHTMLDivElement,HTMLButtonElement, etc. as needed. useRefwith no initial value:useRef<HTMLInputElement>(null)yieldsRefObject<HTMLInputElement>, whose.currentmay benull. Guard with?.before calling methods.useRefwith an initial value:useRef<number>(0)yields a mutable ref where.currentis always a number.- Imperative handles: when you want to expose methods rather than a DOM node, use
useImperativeHandleand type the ref as your handle interface. Refas a prop (React 19+): newer React versions let function components acceptrefdirectly as a prop, soforwardRefis no longer required there. If you are on an older React version, keep usingforwardRef.
forwardRef vs. ref-as-prop
| Situation | Recommended approach |
|---|---|
| React 18 or earlier | forwardRef<Element, Props> |
| React 19+ | ref as a normal prop, typed as Ref<Element> |
| Exposing custom methods | useImperativeHandle with a handle type |
| Passing a ref through several layers | forwardRef at each layer, or ref-as-prop in React 19+ |
Practical next step
If you are unsure which element type to use, hover the JSX element in your editor: the DOM type is usually shown there. For example, hovering <input> reveals HTMLInputElement, which is exactly what the first forwardRef type argument expects.
The React TypeScript Cheatsheet covers forwardRef, refs, ComponentProps, and related patterns in its API reference and "Useful patterns" sections, which is a reasonable place to check the exact signatures once you know which element or handle you are targeting.
How do I type React context and error boundaries in TypeScript?
For both React context and error boundaries, TypeScript's main job is to describe the values that flow through React's runtime, not to change how React behaves. The React TypeScript Cheatsheet treats context, refs, and error boundaries as separate "popular topics," which is a sensible split: each has its own typing shape and pitfalls.
Typing React context
Context has two distinct types: the shape of the value and the type of the context object itself.
- Create the context with a deliberate default.
createContextinfers from its argument, so pass a real default when one exists; otherwise use a nullable default and handle the null case at the consumer. - A common pattern is a null default plus a custom hook. Create the context with
null(orundefined), then write auseContextwrapper that throws when the value is missing. Consumers get a non-nullable value without a!assertion at every call site. - Keep the provider value stable. The type doesn't prevent a new object being created on every render; memoize the value if consumers re-render too often.
- Split contexts when update frequency differs. A state context and a dispatch context typed separately let components subscribe only to what they need.
- Watch the generic inference.
createContext<T>(default)gives youContext<T>; if you omit the generic, a too-narrow default can make later values fail to type-check.
A concrete scenario: a theme provider holding { theme: "light" | "dark"; toggle: () => void }. Type the context as that object, default to null, and expose a useTheme() hook that throws if called outside the provider. Every component then gets autocomplete on theme and toggle and a clear error if the provider is missing.
Typing error boundaries
Error boundaries must be class components, because componentDidCatch and getDerivedStateFromError have no hook equivalents.
- Props are usually just
children: React.ReactNode, plus any fallback or logging props you want to pass down. - State needs an
hasError: booleanfield (and optionally the caught error itself). getDerivedStateFromErroris static, takes the thrown value (typedunknownorError) and returns the state update.componentDidCatchreceives(error: Error, errorInfo: React.ErrorInfo)and is where logging belongs.- Render either the fallback or
children. The fallback can be a prop typed asReact.ReactNodeor a render function if it needs the error.
Practical detail: getDerivedStateFromError receives whatever was thrown, which is not guaranteed to be an Error. Typing it as unknown and normalizing inside is more honest than assuming Error.
If you want the project's own examples of these patterns, the cheatsheet's "Useful patterns by use case" section is the place to start; it is organized by scenario rather than by API, which suits readers who already know the React feature and just need the type signature.
User reviews (0)