TypeScript + React cheatsheet.
Function component
type Props = { name: string; age?: number };
function Hello({ name, age = 0 }: Props) {
return <p>{name}, {age}</p>;
}
Avoid React.FC — implicit children, awkward defaults. Just annotate the prop arg.
children
type Props = { children: React.ReactNode };
function Box({ children }: Props) {
return <div>{children}</div>;
}
React.ReactNode covers strings, numbers, elements, fragments, arrays, null.
For function-as-child:
type Props = { children: (n: number) => React.ReactNode };
Events
function Form() {
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
const onClick: React.MouseEventHandler<HTMLButtonElement> = (e) => { ... };
return (
<form onSubmit={onSubmit}>
<input onChange={onChange} />
<button onClick={onClick}>Go</button>
</form>
);
}
Common event types:
React.FormEvent<T>React.ChangeEvent<T>React.MouseEvent<T>React.KeyboardEvent<T>React.FocusEvent<T>
useState
const [count, setCount] = useState(0); // inferred: number
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<Item[]>([]);
useEffect
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id); // cleanup
}, [tick]);
Effect must return void | (() => void).
useRef
const ref = useRef<HTMLInputElement>(null);
ref.current?.focus();
// Mutable value
const idRef = useRef(0);
idRef.current++;
For DOM refs, init with null and use optional chaining.
useReducer
type State = { count: number };
type Action = { type: "inc" } | { type: "dec" } | { type: "set"; n: number };
function reducer(s: State, a: Action): State {
switch (a.type) {
case "inc": return { count: s.count + 1 };
case "dec": return { count: s.count - 1 };
case "set": return { count: a.n };
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
useContext
const ThemeContext = React.createContext<"light" | "dark" | undefined>(undefined);
function useTheme() {
const t = useContext(ThemeContext);
if (!t) throw new Error("useTheme outside provider");
return t;
}
Default to undefined, throw in the custom hook — guarantees you’re inside provider.
useCallback / useMemo
const onClick = useCallback((id: number) => { ... }, [deps]);
const items = useMemo(() => expensive(data), [data]);
Generics inferred from the function.
Generic component
function List<T>({ items, render }: {
items: T[];
render: (item: T) => React.ReactNode;
}) {
return <ul>{items.map((it, i) => <li key={i}>{render(it)}</li>)}</ul>;
}
<List items={users} render={(u) => u.name} />
In .tsx <T> clashes with JSX — use <T,> or <T extends unknown>.
forwardRef
type Props = { label: string };
const Input = forwardRef<HTMLInputElement, Props>(({ label }, ref) => (
<label>
{label}
<input ref={ref} />
</label>
));
React 19 removed the need — you can pass ref as a prop on regular function components.
Polymorphic component (as prop)
type BoxProps<T extends React.ElementType> = {
as?: T;
children?: React.ReactNode;
} & Omit<React.ComponentPropsWithoutRef<T>, "as" | "children">;
function Box<T extends React.ElementType = "div">({
as,
...props
}: BoxProps<T>) {
const Component = as || "div";
return <Component {...props} />;
}
<Box as="a" href="/x">link</Box>
<Box as="button" type="submit">go</Box>
ComponentProps
type ButtonProps = React.ComponentProps<"button">;
type MyProps = React.ComponentProps<typeof MyComponent>;
Reuses existing element/component prop types.
Style props
const style: React.CSSProperties = {
color: "red",
display: "flex",
// CSS variables (with module augmentation):
"--my-var": "10px",
};
Children types
React.ReactNode // anything renderable
React.ReactElement // JSX element
React.ReactChild // single text/element
React.JSX.Element // JSX element (new)
React.PropsWithChildren<T> // T & { children?: ReactNode }
Discriminated props
type Props =
| { variant: "primary"; onClick: () => void }
| { variant: "link"; href: string };
function Button(p: Props) {
if (p.variant === "primary") return <button onClick={p.onClick} />;
return <a href={p.href} />;
}
Common mistakes
React.FCwith default children — outdated, avoid.anyfor events to avoid figuring out the type.- Spreading without
Omit-ing component-specific props. useRef<HTMLDivElement>()withoutnull— error in strict mode.- Generic component in
.tsx<T>clashing with JSX — use<T,>.
Read this next
If you want my React + TS starter, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .