LogicLoop Logo
LogicLoop
LogicLoop / frontend-frameworks / How to Create Reusable Generic Components in React with TypeScript
frontend-frameworks May 25, 2025 5 min read

Mastering Generic Components in React and TypeScript for Better Developer Experience

Priya Narayan

Priya Narayan

Systems Architect

How to Create Reusable Generic Components in React with TypeScript

When building React applications with TypeScript, one of the most powerful patterns you can leverage is generic components. This approach combines React's component reusability with TypeScript's type safety to create flexible, maintainable code that dramatically improves your developer experience (DX).

If you find yourself creating multiple components with similar structures but different data types, generic components might be exactly what you need. They allow you to create a single component that can work with various data types while maintaining full type safety.

Understanding the Problem: Non-Generic Components

Let's start by looking at a common pattern in React applications: a table component that accepts data rows and a render function for those rows.

A non-generic Table component implementation showing the limitations of using 'any' type in React components
A non-generic Table component implementation showing the limitations of using 'any' type in React components

In this example, we have a Table component that accepts two props: an array of rows and a render function. The component maps over the rows and uses the render function to display each row. However, there's a significant issue with this implementation: the rows are typed as 'any[]', and the render function accepts 'any' as props.

TSX
// Non-generic implementation with 'any' types
interface TableProps {
  rows: any[];
  renderRow: React.FC<any>;
}

const Table = ({ rows, renderRow: RenderRow }: TableProps) => {
  return (
    <table>
      <tbody>
        {rows.map((row, i) => (
          <tr key={i}>
            <RenderRow {...row} />
          </tr>
        ))}
      </tbody>
    </table>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

This approach has several problems:

  • No type safety: TypeScript can't verify that the render function handles the correct type of data
  • No autocompletion: When using the render function, you don't get intellisense for the properties available on each row
  • Maintenance challenges: If the row structure changes, TypeScript won't help you identify places that need updates
  • No relationship between rows and renderRow: TypeScript doesn't enforce that the render function can handle the row type

You might try to fix this by manually specifying the types, but that creates duplication and doesn't guarantee type consistency between the rows and render function.

The Solution: Generic React Components

Generic components solve these problems by creating a relationship between the types of your props. Here's how we can transform our Table component into a generic component:

A generic Table component implementation in React and TypeScript showing proper type relationships between props
A generic Table component implementation in React and TypeScript showing proper type relationships between props
TSX
// Generic component implementation
interface TableProps<TRow extends Record<string, any>> {
  rows: TRow[];
  renderRow: React.FC<TRow>;
}

const Table = <TRow extends Record<string, any>,>({ 
  rows, 
  renderRow: RenderRow 
}: TableProps<TRow>) => {
  return (
    <table>
      <tbody>
        {rows.map((row, i) => (
          <tr key={i}>
            <RenderRow {...row} />
          </tr>
        ))}
      </tbody>
    </table>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Key Elements of a Generic React Component

  1. Type Parameter: The syntax defines a type parameter that can be inferred or explicitly provided
  2. Type Constraint: The 'extends Record' part ensures TRow must be an object (necessary for spreading props)
  3. Type Relationship: Both 'rows' and 'renderRow' use the same TRow type, creating a relationship between them
  4. Type Inference: TypeScript automatically infers TRow from the 'rows' prop you provide

With this implementation, TypeScript can now ensure that the render function receives the same type of data that exists in the rows array. This creates a powerful type-safe relationship between your props.

Using the Generic Table Component

Now let's see how to use our generic Table component:

TSX
// Using the generic Table component
type User = {
  name: string;
  id: number;
};

// TypeScript will infer the User type for TRow
const UserTable = () => (
  <Table
    rows={[
      { name: "Matt", id: 1 },
      { name: "Sarah", id: 2 }
    ]}
    renderRow={({ name, id }) => (
      <td>{id}: {name}</td>
    )}
  />
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Notice that we don't need to explicitly specify the type parameter - TypeScript infers it from the rows we provide. The renderRow function now receives properly typed props, giving us autocompletion and type checking.

Benefits of Generic React Components

  • Type Safety: TypeScript ensures that your component is used correctly with compatible types
  • Better Developer Experience: Get autocompletion and type checking for props
  • Reusability: Create a single component that works with different data types
  • Maintenance: Changes to data structures are caught by the type system
  • Self-documenting: The types serve as documentation for how the component should be used

Common Use Cases for Generic Components in React

Generic components are particularly useful in these scenarios:

  • Data display components (tables, lists, grids)
  • Form components that work with different data models
  • Higher-order components that preserve the props type of wrapped components
  • Reusable UI components that need to work with various data structures
  • API integration components that handle different response types

Advanced Generic Component Patterns

Once you understand the basics, you can apply more advanced patterns with generic components:

Multiple Type Parameters

TSX
// Component with multiple type parameters
const DataFetcher = <TData, TError>({ 
  url, 
  onSuccess, 
  onError 
}: {
  url: string;
  onSuccess: (data: TData) => void;
  onError: (error: TError) => void;
}) => {
  // Implementation
  return null;
};
1
2
3
4
5
6
7
8
9
10
11
12
13

Default Type Parameters

TSX
// Component with default type parameter
const List = <T = string,>({ 
  items, 
  renderItem 
}: {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}) => {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Common Challenges and Solutions

Challenge: JSX Syntax Confusion

The angle brackets used for generics can conflict with JSX syntax. To resolve this, add a trailing comma inside the angle brackets:

TSX
// Without trailing comma - might cause syntax issues
const Component = <T>() => {};

// With trailing comma - works reliably in TSX files
const Component = <T,>() => {};
1
2
3
4
5

Challenge: Type Constraints

Sometimes you need to ensure the generic type meets certain requirements. Use the 'extends' keyword to add constraints:

TSX
// Ensure T has an id property
const IdentifiableList = <T extends { id: string | number },>({ 
  items 
}: {
  items: T[];
}) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{JSON.stringify(item)}</li>
      ))}
    </ul>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Conclusion

Generic components in React with TypeScript provide a powerful way to create reusable, type-safe components. By defining type parameters and establishing relationships between props, you can create components that are both flexible and safe to use.

When you find yourself creating multiple similar components that differ only in the types of data they handle, consider refactoring to a generic component. Your future self (and your team) will thank you for the improved developer experience, reduced code duplication, and enhanced type safety.

Start incorporating generic components into your React and TypeScript projects today to write more maintainable, type-safe code that scales with your application's complexity.

Let's Watch!

How to Create Reusable Generic Components in React with TypeScript

Ready to enhance your neural network?

Access our quantum knowledge cores and upgrade your programming abilities.

Initialize Training Sequence
L
LogicLoop

High-quality programming content and resources for developers of all skill levels. Our platform offers comprehensive tutorials, practical code examples, and interactive learning paths designed to help you master modern development concepts.

© 2025 LogicLoop. All rights reserved.