How to Type Generic React Components

Type generic React components so their props depend on the data type at each call site. Learn function generics and the arrow function comma.

5 min read

A generic React component takes a type parameter so its props can depend on the data type at each call site. Write the type parameter on the function, and TypeScript flows it through the props and infers it from usage. Without a generic, the item type would have to be broad, which loses type safety.

What a generic component is

A List that renders items of any type needs a generic. The items and the render callback both depend on the item type:

App.tsxApp.tsx
type ListProps<T> = {
  items: T[];
  getKey: (item: T) => string;
  render: (item: T) => React.ReactNode;
};

T is a placeholder for the item type. The component does not know what T is until a caller supplies a list. If the basics of typing props are new, start there before adding a type parameter.

Declare the generic on the function

Add the type parameter to the function so TypeScript can infer it from the props:

App.tsxApp.tsx
function List<T>({ items, getKey, render }: ListProps<T>) {
  return (
    <ul>
      {items.map((item) => (
        <li key={getKey(item)}>{render(item)}</li>
      ))}
    </ul>
  );
}

A caller passes a list of users, and TypeScript infers T as the user type. The render callback then receives that user with full autocomplete. Callers rarely write the type parameter themselves, because it comes from the items array.

A typed caller

At the call site, the generic locks to the data type:

App.tsxApp.tsx
type User = { id: string; name: string };
const users: User[] = [{ id: "a1", name: "Maya" }];
 
function App() {
  return (
    <List
      items={users}
      getKey={(user) => user.id}
      render={(user) => user.name}
    />
  );
}

Here T is User, so user.id and user.name are typed in both callbacks. Passing a list of numbers instead would infer a different T for that call. The component stays the same; only the inferred type changes.

The arrow function comma

Function declarations read the generic cleanly. Arrow functions need a trailing comma in the type parameter list inside a .tsx file:

App.tsxApp.tsx
const List = <T,>({ items, getKey, render }: ListProps<T>) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={getKey(item)}>{render(item)}</li>
      ))}
    </ul>
  );
};

Without the comma, TypeScript can mistake the generic for a JSX tag. The comma disambiguates it. Prefer the function declaration form, and use the comma when an arrow function is required.

The comma is only needed in .tsx files, because .ts files have no JSX to confuse the parser.

Why a generic beats any

Using any for the item type silences TypeScript instead of checking it. A generic keeps the safety while staying flexible.

  • any: no checking on the item.
  • A union: limited to listed types.
  • A generic: checked at each call site.

The generic is the only option that stays precise for every data type a caller passes. That is why the type parameter sits on the function rather than on a fixed prop type.

What to learn next

Generics power the polymorphic component pattern. See how the same ideas type a polymorphic component whose tag the parent chooses.

Rune AI

Rune AI

Key Insights

  • Add a type parameter to the component function.
  • Flow the parameter through the props type.
  • TypeScript infers the parameter at each call site.
  • Function declarations read generics cleanly.
  • Arrow functions need a trailing comma in .tsx.
RunePowered by Rune AI

Frequently Asked Questions

What is a generic React component?

A component with a type parameter. Its props can depend on the type passed or inferred at each call site, so one component works with many data types.

How do I declare a generic on a function component?

Add the type parameter to the function, like function List<T>(props). TypeScript infers T from the props at each call site.

Why do arrow functions need a trailing comma in .tsx files?

In .tsx, a lone type parameter can be parsed as a JSX tag. Writing <T,> with a trailing comma tells TypeScript it is a generic.

Conclusion

Type a generic React component by adding a type parameter to the function and flowing it through the props. Function declarations are cleanest, and arrow functions need a trailing comma in .tsx files.