π¦ React Generics & Interfaces β Build Flexible & Type-Safe Components (2025 Guide)
π§² Introduction β Why Use Generics & Interfaces in React?
When building scalable React applications, you need components and hooks that can handle different data types while maintaining type safety. TypeScriptβs Generics and Interfaces help you:
- π¦ Build reusable components (like lists, tables, and forms)
- βοΈ Create flexible custom hooks
- π Maintain strong typings across various use cases
π― In this guide, youβll learn:
- How to define and use generics in React components and hooks
- When to use
interfacevstype - Best practices for building maintainable, generic UIs
π€ 1. What Are Generics in TypeScript?
Generics are type variables that allow you to parameterize a component, function, or hook over one or more types.
β Basic Example:
function identity<T>(value: T): T {
return value;
}
π You can now call identity<string>('React') or identity<number>(42)
β
Keeps logic flexible without losing type safety
π§± 2. Generic React Functional Component
type ItemListProps<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
};
function ItemList<T>({ items, renderItem }: ItemListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
β Usage:
<ItemList
items={['React', 'Vue', 'Svelte']}
renderItem={(item) => <strong>{item}</strong>}
/>
β
Works for string[], User[], Product[], etc.
β
Type-safe rendering of any data structure
π§ͺ 3. Typing Custom Hooks with Generics
function useArray<T>(initial: T[]): [T[], (item: T) => void] {
const [array, setArray] = useState(initial);
const push = (item: T) => setArray((a) => [...a, item]);
return [array, push];
}
β Usage:
const [names, addName] = useArray<string>([]);
addName('React');
π Makes hooks reusable for any type
β
Promotes DRY logic across features
π 4. Interface vs Type β When to Use
| Feature | interface | type |
|---|---|---|
| Extensibility | β Can be extended | β
Can use intersection (&) |
| Declaration merging | β Supported | β Not supported |
| Unions | β Not supported directly | β Works well with union types |
| Use case | Objects, classes | Functions, primitives, unions |
β Example:
interface User {
id: number;
name: string;
}
type Admin = User & { role: 'admin' };
π Use interface for public APIs and extending objects
π Use type for unions, aliases, and function signatures
π§© 5. Generics with Form Components
type InputProps<T> = {
value: T;
onChange: (value: T) => void;
};
function Input<T>({ value, onChange }: InputProps<T>) {
return (
<input
value={String(value)}
onChange={(e) => onChange(e.target.value as T)}
/>
);
}
β
Type-safe for string, number, etc.
π Avoid casting where possibleβuse overloads for better safety
π 6. Generic Component with Constraints
type WithId = { id: string | number };
function ListWithKey<T extends WithId>({ items }: { items: T[] }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{JSON.stringify(item)}</li>
))}
</ul>
);
}
β
T extends WithId enforces that all items have id
β
Prevents misuse at compile time
π§ 7. Generic Context Provider Example
const createGenericContext = <T extends unknown>() => {
const context = React.createContext<T | undefined>(undefined);
const useGenericContext = () => {
const value = useContext(context);
if (!value) throw new Error('Context not found');
return value;
};
return [context.Provider, useGenericContext] as const;
};
π Build reusable contexts for themes, auth, or data sources
β
Fully type-safe access to shared data
π Best Practices
| Best Practice | Why It Helps |
|---|---|
| β Use generics for reusable logic | Avoids duplication of similar code |
β
Use constraints (extends) | Prevents misused types |
β
Use type for flexible component props | Easier to combine and extend |
β
Prefer interface for objects only | Enables declaration merging |
β
Combine with React.memo or FC as needed | Works well in scalable systems |
π Summary β Recap & Next Steps
TypeScript generics and interfaces empower React developers to write reusable, type-safe, and scalable components. Whether you’re typing props, hooks, or contextsβgenerics make your code DRY and robust.
π Key Takeaways:
- Use
<T>to make components/data generic - Combine generics with constraints (
extends) to enforce shape - Use
typefor unions and props,interfacefor objects - Create reusable hooks, lists, forms, and contexts with generics
βοΈ Real-World Relevance:
Used in component libraries like Chakra UI, Radix UI, and design systems like Microsoft Fluent UI for consistent, type-safe APIs.
β FAQ Section
β Can I use generics with React.FC?
β
Yes, but avoid FC for performance-critical or overly generic cases. Prefer explicitly typed functions.
β Whatβs the best way to type reusable form inputs?
β
Use a generic prop (<T>) and define event/value typings accordingly. You can also combine with Zod/Yup for validation.
β Should I prefer type or interface?
β
Use type for component props, unions, and primitives. Use interface for extending object shapes or third-party integrations.
β Can generics be used in JSX elements?
β
Yes, but the syntax can be tricky. Use parentheses or helper components to avoid ambiguity:
<ItemList<string> items={['a', 'b']} renderItem={...} />
β Whatβs the benefit of constraints in generics?
β
They ensure generic types meet required conditions (e.g., extends { id: string }), reducing runtime errors.
Share Now :
