DesignDev.io logoDesignDev.io logo

  1. Home
  2. /React Foundations
  3. /TypeScript Generics in React: The Patterns You'll Use Every Week
← Back to home

TypeScript Generics in React: The Patterns You'll Use Every Week

Alex Chen

Alex Chen

Cover Image for TypeScript Generics in React: The Patterns You'll Use Every Week
Alex Chen

Alex Chen

January 14, 2026·3 min read
Updated April 26, 2026
Series:React Foundations
Level:Intermediate
Tags:reactweb
Assumes:Comfortable with TypeScript basics, React hooks, and writing typed components

TypeScript generics in React feel intimidating right up until the moment they don't. The syntax is the hard part — the patterns themselves are straightforward once you see them three or four times. Here are the five you'll reach for every week.


Most TypeScript developers hit generics in React at the same moment: they're trying to type a component that works with any data, not just one specific shape, and any feels wrong. They look at the syntax — <T extends object> inside angle brackets that already have JSX angle brackets — and close the tab.

The syntax is genuinely awkward. JSX and TypeScript generics both use <>, and the TypeScript compiler can't always tell which is which. But the patterns themselves are simple once you separate the concept from the syntax noise.

Five patterns. These are the ones you'll actually use.


Pattern 1: Generic components

A List component that renders any array of items. Without generics, you'd write one for users, one for products, one for notifications. With generics, you write one that works for all of them.

The T is a type parameter — a placeholder for whatever type the caller passes in. When you use List, TypeScript infers T from what you pass to items:

user inside renderItem is typed as User automatically. If you try to access user.nonexistent, TypeScript catches it.

The JSX syntax problem. In a .tsx file, <T> looks like a JSX element. The TypeScript compiler gets confused. Two solutions:

The constraint approach is cleaner and more common — it also communicates that T must be an object, which is usually true for React list items anyway.


Pattern 2: Generic hooks

The hooks from Article #4 used generics already — useLocalStorage<T> and useDebounce<T>. Here's why and how.

A useLocalStorage hook without generics:

TypeScript infers T from initialValue:

The as const at the return ensures the tuple type is preserved — without it, TypeScript infers Array<T | Function> instead of [T, (value: T) => void].


Pattern 3: ComponentProps — extracting prop types

You have a third-party component or a component you've written. You want to pass all its props through another component without rewriting the entire type definition.

Every native button prop — onClick, type, aria-label, form, name — is automatically available on CustomButton without being explicitly declared. When the browser adds a new attribute or you need to pass something obscure, it just works.

ComponentPropsWithoutRef<'button'> is the version that strips the ref prop — useful when you're not forwarding refs. ComponentPropsWithRef<'button'> explicitly includes it.


Pattern 4: The as prop — polymorphic components

A Text component that renders as h1, h2, p, span, or any other HTML element — with the correct props for whichever element it becomes.

This is the pattern design systems use for components like <Box>, <Text>, and <Heading>.

Usage:

The type machinery here is more complex than the other patterns — but it's worth understanding because it's the foundation of every well-typed design system component. The key insight: C is the element type, and React.ComponentPropsWithoutRef<C> gives you the props for that specific element.


Pattern 5: Constraining generics

Sometimes T can be anything. Sometimes it needs to have specific properties. Constraints let you require structure without locking in a specific type.

Now SelectList works with any object that has id and label — but TypeScript knows what option is inside onChange, preserving all the extra properties:

Without the constraint, T could be anything — including a primitive that has no id. With T extends { id: string; label: string }, TypeScript enforces the minimum shape while still preserving the full type of whatever you pass in.


Putting it together: a typed data table

Here's a component that combines Pattern 1 (generic component), Pattern 3 (ComponentProps), and Pattern 5 (constrained generics) into something you'd actually ship:

Used like this:

TypeScript infers T = Article from the data prop. The key field in each column definition is typed as keyof Article — so passing key: 'nonexistent' is a compile-time error. The render function receives the correctly typed value for that column and the full row as Article.

One component. Works for any data shape with an id. Fully typed at every call site.


The syntax cheat sheet


Up next: Compound components — the design pattern that changed how I build UIs — the context-powered pattern behind every well-designed component API.

Related: TypeScript utility types you actually use — the built-in types that compose with generics to eliminate boilerplate.

Alex Chen is a senior frontend engineer who writes about React patterns, JavaScript internals, and the decisions that separate maintainable codebases from ones that fight back. Opinionated by design.

Photo by Peaky Frames on Unsplash


More Stuff

Publishing Your First React Component to npm — the Complete Setup
April 1, 2026·3 min read

Publishing Your First React Component to npm — the Complete Setup

Most tutorials stop at "it works on my machine." This one doesn't. Publishing to npm is where your library becomes real — installable, versioned, and usable by anyone. Here's the complete setup, including the fields most developers get wrong in package.json and how to inspect what you're actually shipping before it goes live.

Muhammad Athar

Muhammad Athar

How to structure a UI library that scales — folders, exports, and naming conventions
April 1, 2026·3 min read

How to structure a UI library that scales — folders, exports, and naming conventions

The folder structure you pick on day one is the one you'll live with across 40 components. Get it wrong and you'll be fighting your own codebase by article 10. Get it right and adding a new component takes five minutes and follows a pattern every contributor can learn in one sitting.

Muhammad Athar

Muhammad Athar

DesignDev.io logoDesignDev.io logo

Explore

  • Home
  • Search
  • Authors
  • Series
  • About Us

Account

  • Sign in

Legal

  • Privacy Policy
  • Terms of Service

© 2026 DesignDev.io · All rights reserved

Built with Next.js · Tailwind · Sanity · Vercel