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.FC with default children — outdated, avoid.
  • any for events to avoid figuring out the type.
  • Spreading without Omit-ing component-specific props.
  • useRef<HTMLDivElement>() without null — 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 .