Create generic functional React components with TypeScript

René Kulik on 19.11.2023

Introduction

React in combination with TypeScript is an effective way of building modern web applications. As TypeScript supports generics, those can also be used for React components. In this blog post, I am going to demonstrate the concept of generic functional React components with TypeScript. As a practical example we will create a generic list component that is able to render any kind of items.

What are generics?

Generics in TypeScript empore functions, components and classes to work with different types of data without loosing their type safety. They yield a technique to design software as flexible and reusable as possible.

Generic list component

To illustrate the concept of generic components we are going to implement a list that can render items of various data types. In our case fruits.

Generic type

To define the API of the generic list component we create a GenericListProps type that contains the following properties:

Property Description
items The array of items to render
createKey A function to create the special key attribute you need to include when creating a list
renderItems A function that returns a ReactNode based on the current item

GenericListProps has to be parameterized to support items of different types of data. Therefore, we apply a TypeScript generic, denoted as <T>. This generic is used throughout GenericListProps:

import { Key, ReactNode } from 'react';

type GenericListProps<T> = {
  items: T[];
  createKey: (item: T) => Key;
  renderItem: (item: T) => ReactNode;
};

Through that, items, createKey and renderItem are not limited to a specific type.

Component

After defining GenericListProps, let’s focus on the corresponding GenericList component. Besides typehinting its props with GenericListProps<T>, the same type parameter T is used to make the component generic itself:

export const GenericList = <T,>(props: GenericListProps<T>) => {
  const { items, createKey, renderItem } = props;

  return (
    <ul>
      {items.map(item => (
        <li key={createKey(item)}>{renderItem(item)}</li>
      ))}
    </ul>
  );
};

Usage

const FRUITS = [
  { id: 1, name: 'Apple', color: '#ff0000' },
  { id: 2, name: 'Avocado', color: '#00ff00' },
  { id: 3, name: 'Blueberry', color: '#4f86f7' },
];

export const Fruits = () => (
  <GenericList
    items={FRUITS}
    createKey={fruit => fruit.id}
    renderItem={fruit => <span style={{ color: fruit.color }}>{fruit.name}</span>}
  />
);

In the example above, the GenericList component is used to render different fruits:

To create the special key attribute, createKey is given a function that returns the ID of each respective fruit. The function passed into renderItem is creating a span colorized based on the color of the fruit.

Conclusion

In this blog post I described how TypeScript generics can be used to build functional React components. It is a powerful tool that helps reducing code and enables components to handle a variety of data types, making them highly flexible and reusable.