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:
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:
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:
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:
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
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.
Frequently Asked Questions
What is a generic React component?
How do I declare a generic on a function component?
Why do arrow functions need a trailing comma in .tsx files?
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.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.