CodingCode ReviewAdvanced45 minSaves 1 hour

Typed React Compound Table Component with Generics

Frontend developers building design systems can create a type-safe React compound table component. It uses TypeScript generics to ensure column type integrity, crucial for robust data displays.

This prompt outlines the design and implementation of a React compound table component, including Table.Row and Table.Cell, with TypeScript generics. It focuses on preserving column-specific types throughout the component structure, enhancing type safety and developer experience for complex data tables in design systems.

READY-TO-USE PROMPT

Copy Prompt

prompt.txt
Role: Senior Frontend Engineer specializing in TypeScript and React component architecture.

Context: Our team is developing a new design system, and a critical component is a highly flexible and type-safe data table. We need a compound component structure (e.g., `Table`, `Table.Header`, `Table.Row`, `Table.Cell`) that leverages TypeScript generics to ensure column data types are preserved and correctly inferred throughout the component hierarchy. This is essential for maintainability, developer experience, and preventing runtime type errors.

Task: Design and implement a `{{table_name}}` compound component in React with TypeScript. The component must be generic over the data type it displays and capable of inferring and preserving the types of individual columns. Provide the necessary interfaces, component definitions, and usage examples.

Constraints:
*   The primary component, `{{table_name}}`, must accept a generic type `T` representing the shape of a single data row.
*   `{{table_name}}.Header` should accept a generic `T` and define column headers, potentially with custom renderers.
*   `{{table_name}}.Row` should accept a generic `T` and render a single row of data.
*   `{{table_name}}.Cell` should accept a generic `T` and a `keyof T` to access the specific column data, ensuring type safety for that cell's content. It should also support an optional custom renderer prop.
*   The component API should be intuitive for consumers, allowing them to easily define columns and render data while benefiting from strong type checking.
*   Include a basic example of how to use the component with a sample data structure, like `{{example_data_structure}}`.
*   Consider edge cases such as empty data sets or columns with `undefined`/`null` values.

Output:
Provide the following:
1.  **File Structure and Code Blocks**: Present the TypeScript/React code for the `{{table_name}}` component, including all sub-components (`Header`, `Row`, `Cell`), interfaces, and types. Organize it into logical file paths (e.g., `src/components/{{table_name}}/index.tsx`, `src/components/{{table_name}}/types.ts`).
2.  **Short Explanation**: Briefly explain the key design decisions, especially how generics are used to achieve type safety.
3.  **Edge Cases**: Discuss how the component handles common edge cases, such as missing data or custom rendering requirements.
4.  **Tests Outline**: Provide a high-level outline of unit tests for the component, focusing on type correctness and rendering behavior.

Estimated results

DifficultyAdvanced
Setup time45 min
Time saved1 hour
Best modelsClaude, ChatGPT, Gemini
Best audienceSoftware Development, Web Development

Editor's note

Why this prompt matters

Building robust design systems often involves creating complex UI components that need to handle diverse data types while maintaining strict type safety. A common challenge arises with data tables, where ensuring column data types are preserved across different components—from the main table to individual cells—can become unwieldy without a disciplined approach. This workflow addresses that specific pain point for frontend developers tasked with architecting shared UI libraries.

This prompt is designed for engineers who need to implement a data table compound component that integrates within a TypeScript-first React environment. It focuses on using generics to infer and enforce data types at every level of the component hierarchy, from the overall data structure down to individual cell values. Reaching for this pattern ensures that data integrity is maintained throughout the rendering process, significantly reducing the likelihood of runtime type errors and improving the overall developer experience for anyone consuming the component. It's particularly useful when developing components intended for broad reuse across multiple applications.

Anatomy

Prompt engineering breakdown

Role

Senior Frontend Engineer specializing in TypeScript and React component architecture.

Context

The team needs a flexible, type-safe data table for a new design system. It must be a compound component (Table, Table.Header, Table.Row, Table.Cell) using TypeScript generics to preserve column data types. This ensures maintainability, developer experience, and prevents runtime errors.

Goal

To design and implement a {{table_name}} compound component in React with TypeScript. The component needs to be generic over the data type T and infer/preserve individual column types, providing necessary interfaces, component definitions, and usage examples.

Constraints

The {{table_name}} component must accept a generic T. Table.Header and Table.Row should also accept T. Table.Cell requires T and keyof T for type-safe column access, supporting custom renderers. The API must be intuitive, include a usage example with {{example_data_structure}}, and address edge cases like empty data or null/undefined values.

Output format

The output must include organized TypeScript/React code blocks with file paths, a brief explanation of design choices (especially generics), a discussion of edge case handling, and a high-level outline for unit tests focusing on type correctness.

Why this structure works

This structured prompt works by first establishing a clear role for the AI, which primes it to respond with an expert-level understanding of React and TypeScript best practices. The explicit constraints detail the required component structure and type safety mechanisms, guiding the model toward a precise solution. Finally, the structured output explicitly requests code, explanations, edge cases, and tests, ensuring a comprehensive and actionable response.

Pick your version

Prompt variations

BeginnerWorks with any model

When you are new to TypeScript generics in React and need a straightforward example to understand the core concepts.

prompt.txt
As a junior frontend developer, I'm learning to build reusable React components with TypeScript. I need help creating a simple `{{table_name}}` component that can display a list of items, like `{{example_data_structure}}`. Show me how to use basic TypeScript to make sure the data types for each column are correct when I display them in the table. I want a `Table` component, a `TableHeader`, `TableRow`, and `TableCell`. Please provide the code for these components and a simple way to use them. Focus on making sure the types are safe for the data in the columns. Also, explain why your approach helps prevent mistakes when showing data.
ProfessionalBest with claude

When developing core components for a design system, requiring robust type safety and adherence to modern React/TypeScript patterns.

prompt.txt
Role: Senior Frontend Engineer specializing in TypeScript and React component architecture.

Context: Our team is developing a new design system, and a critical component is a highly flexible and type-safe data table. We need a compound component structure (e.g., `Table`, `Table.Header`, `Table.Row`, `Table.Cell`) that leverages TypeScript generics to ensure column data types are preserved and correctly inferred throughout the component hierarchy. This is essential for maintainability, developer experience, and preventing runtime type errors.

Task: Design and implement a `{{table_name}}` compound component in React with TypeScript. The component must be generic over the data type it displays and capable of inferring and preserving the types of individual columns. Provide the necessary interfaces, component definitions, and usage examples.

Constraints:
*   The primary component, `{{table_name}}`, must accept a generic type `T` representing the shape of a single data row.
*   `{{table_name}}.Header` should accept a generic `T` and define column headers, potentially with custom renderers.
*   `{{table_name}}.Row` should accept a generic `T` and render a single row of data.
*   `{{table_name}}.Cell` should accept a generic `T` and a `keyof T` to access the specific column data, ensuring type safety for that cell's content. It should also support an optional custom renderer prop.
*   The component API should be intuitive for consumers, allowing them to easily define columns and render data while benefiting from strong type checking.
*   Include a basic example of how to use the component with a sample data structure, like `{{example_data_structure}}`.
*   Consider edge cases such as empty data sets or columns with `undefined`/`null` values.

Output:
Provide the following:
1.  **File Structure and Code Blocks**: Present the TypeScript/React code for the `{{table_name}}` component, including all sub-components (`Header`, `Row`, `Cell`), interfaces, and types. Organize it into logical file paths (e.g., `src/components/{{table_name}}/index.tsx`, `src/components/{{table_name}}/types.ts`).
2.  **Short Explanation**: Briefly explain the key design decisions, especially how generics are used to achieve type safety.
3.  **Edge Cases**: Discuss how the component handles common edge cases, such as missing data or custom rendering requirements.
4.  **Tests Outline**: Provide a high-level outline of unit tests for the component, focusing on type correctness and rendering behavior.
Short VersionWorks with any model

For quick prototyping or when you need a barebones example to jumpstart development, assuming familiarity with the pattern.

prompt.txt
Design a `{{table_name}}` React compound component with TypeScript generics. It must handle a generic data type `T` for rows and ensure type safety for individual columns through sub-components like `Table.Header`, `Table.Row`, and `Table.Cell`. `Table.Cell` should accept `keyof T` and support custom renderers. Provide the component code, interfaces, and a usage example with `{{example_data_structure}}`, demonstrating how column types are preserved and inferred. Discuss how to handle empty data sets.
EnterpriseBest with chatgpt

For large-scale enterprise applications where component stability, long-term maintenance, and adherence to corporate standards are paramount.

prompt.txt
As the lead architect for our enterprise design system, I require a highly resilient and compliant `{{table_name}}` compound component in React with TypeScript generics. This component must guarantee end-to-end type safety for data rows (type `T`) and individual columns, supporting complex data structures like `{{example_data_structure}}`. The design should prioritize extensibility, accessibility (WCAG 2.1), and performance for large datasets. Include robust error handling for edge cases (e.g., null/undefined data, empty arrays) and a clear strategy for future enhancements. Provide the complete component implementation, detailed architectural rationale, a plan for integrating with existing tooling, and a comprehensive test suite outline that covers functional, accessibility, and type validation aspects to ensure long-term maintainability and regulatory compliance across our product portfolio.

What you'll get

Expected output

// src/components/DataTable/types.ts export interface ColumnDef<T> { key: keyof T; header: string; render?: (item: T) => React.ReactNode; }

export interface DataTableProps<T> { data: T[]; columns: ColumnDef<T>[]; children: React.ReactNode; // For compound component structure }

export interface DataTableRowProps<T> { item: T; columns: ColumnDef<T>[]; children?: React.ReactNode; // Allows custom row content, or defaults to cells }

export interface DataTableColumnHeaderProps<T> { column: ColumnDef<T>; }

export interface DataTableColumnCellProps<T> { item: T; column: ColumnDef<T>; }

// src/components/DataTable/index.tsx import React, { createContext, useContext } from 'react'; import { ColumnDef, DataTableProps, DataTableRowProps, DataTableColumnHeaderProps, DataTableColumnCellProps, } from './types';

interface DataTableContextType<T> { data: T[]; columns: ColumnDef<T>[]; }

const DataTableContext = createContext<DataTableContextType<any> | undefined>(undefined);

function useDataTableContext<T>() { const context = useContext(DataTableContext); if (!context) { throw new Error('DataTable compound components must be rendered within a <DataTable> component'); } return context as DataTableContextType<T>; }

const DataTable = <T,>({ data, columns, children }: DataTableProps<T>) => { return ( <DataTableContext.Provider value={{ data, columns }}> <table> {children} </table> </DataTableContext.Provider> ); };

const DataTableHeader = <T,>() => { const { columns } = useDataTableContext<T>(); return ( <thead> <tr> {columns.map((column) => ( <DataTable.HeaderCell key={String(column.key)} column={column} /> ))} </tr> </thead> ); };

const DataTableHeaderCell = <T,>({ column }: DataTableColumnHeaderProps<T>) => { return <th>{column.header}</th>; };

const DataTableBody = <T,>() => { const { data, columns } = useDataTableContext<T>(); return ( <tbody> {data.length === 0 ? ( <tr> <td colSpan={columns.length}>No data available.</td> </tr> ) : ( data.map((item, index) => ( <DataTable.Row key={index} item={item} columns={columns} /> )) )} </tbody> ); };

const DataTableRow = <T,>({ item, columns }: DataTableRowProps<T>) => { return ( <tr> {columns.map((column) => ( <DataTable.Cell key={String(column.key)} item={item} column={column} /> ))} </tr> ); };

const DataTableColumnCell = <T,>({ item, column }: DataTableColumnCellProps<T>) => { const cellContent = column.render ? column.render(item) : (item[column.key] as React.ReactNode); return <td>{cellContent ?? '-'}</td>; // Handle null/undefined gracefully };

DataTable.Header = DataTableHeader; DataTable.HeaderCell = DataTableHeaderCell; DataTable.Body = DataTableBody; DataTable.Row = DataTableRow; DataTable.Cell = DataTableColumnCell;

export { DataTable };

// src/App.tsx (Example Usage) import React from 'react'; import { DataTable } from './components/DataTable';

interface Product { id: string; name: string; category: string; price: number; stock: number | null; }

const products: Product[] = [ { id: 'p1', name: 'Laptop Pro', category: 'Electronics', price: 1200, stock: 15 }, { id: 'p2', name: 'Keyboard Mech', category: 'Electronics', price: 150, stock: 0 }, { id: 'p3', name: 'Monitor Ultra', category: 'Peripherals', price: 400, stock: 5 }, { id: 'p4', name: 'Mouse Ergo', category: 'Peripherals', price: 75, stock: null }, ];

const productColumns = [ { key: 'id', header: 'Product ID' }, { key: 'name', header: 'Product Name' }, { key: 'category', header: 'Category' }, { key: 'price', header: 'Price', render: (product: Product) => $${product.price.toFixed(2)} }, { key: 'stock', header: 'Stock', render: (product: Product) => ( <span style={{ color: product.stock === 0 ? 'red' : 'green' }}> {product.stock === null ? 'N/A' : product.stock} </span> ), }, ];

function App() { return ( <div style={{ padding: '20px' }}> <h1>Product List</h1> <DataTable<Product> data={products} columns={productColumns}> <DataTable.Header /> <DataTable.Body /> </DataTable>

<h2>Empty Data Example</h2> <DataTable<Product> data={[]} columns={productColumns}> <DataTable.Header /> <DataTable.Body /> </DataTable> </div> ); }

export default App;

Short Explanation: The core of this design relies on TypeScript generics, specifically DataTable<T>. The generic type T is passed down through the DataTableContext to all sub-components. This allows ColumnDef<T> to correctly type keyof T, ensuring that only valid keys for the data object T can be specified. Custom render functions in ColumnDef and DataTable.Cell also receive T, providing full type inference for the data item being rendered. This cascade of generics ensures that type safety is maintained from the top-level data prop down to the individual cell rendering, preventing common type mismatches.

Edge Cases:

  1. Empty Data Sets: The DataTable.Body component explicitly checks data.length === 0 and renders a "No data available" message spanning all columns, improving user experience.
  2. `undefined`/`null` Values: DataTable.Cell uses the nullish coalescing operator (?? '-') to display a hyphen for null or undefined cell values when no custom renderer is provided. Custom renderers offer full control for specific handling (e.g., N/A for null stock).
  3. Custom Renderers: Both ColumnDef and DataTable.Cell support a render prop. This allows consumers to define how specific column data should be displayed, enabling rich UI elements or formatted values (e.g., currency, status indicators) while preserving the underlying data type.

Tests Outline:

  • Type Safety Tests:

* Verify that DataTable correctly infers T from the data prop. * Ensure ColumnDef.key only accepts valid keys of T. * Test that DataTable.Cell's item prop is correctly typed as T. * Confirm custom render functions receive the correct T type for their item argument. * Assert that providing an invalid key to ColumnDef results in a TypeScript error.

  • Rendering Tests:

* Render DataTable with sample data and verify all headers and data cells are present. * Test custom render functions are correctly applied to format cell content. * Verify the "No data available" message appears when data is an empty array. * Check how null or undefined values are rendered by default and with custom renderers. * Ensure DataTable.Row and DataTable.Cell correctly display data for each item and column.

  • Compound Component Structure Tests:

* Verify that DataTable.Header, DataTable.Body, DataTable.Row, DataTable.Cell throw an error if used outside of DataTable. * Ensure context propagation works as expected.

Under the hood

Why this prompt works

This prompt structure guides the AI to produce a high-quality, architecturally sound solution by employing several targeted prompt engineering techniques.

Firstly, role priming (Role: Senior Frontend Engineer specializing in TypeScript and React component architecture.) establishes a clear persona. This instructs the model to generate code and explanations consistent with an experienced engineer's perspective, focusing on best practices, maintainability, and advanced TypeScript patterns rather than a basic functional example.

Secondly, explicit constraints are used extensively. By detailing the requirements for DataTable, DataTable.Header, DataTable.Row, and DataTable.Cell, including their generic type T and how keyof T should be used, the prompt precisely defines the desired API and type relationships. This specificity prevents the model from diverging into less type-safe or less flexible designs. The inclusion of edge cases like empty data and null/undefined values further pushes the model to consider real-world scenarios.

Finally, the structured output requirement ensures a comprehensive response. Requesting "File Structure and Code Blocks," "Short Explanation," "Edge Cases," and "Tests Outline" forces the model to not only write the code but also to articulate its design rationale, consider potential issues, and propose validation strategies. This approach moves beyond a simple code generation request, yielding a complete solution brief that is immediately useful for a development team.

Model fit

Best AI models for this prompt

Claude

Claude excels at understanding and generating complex TypeScript generics and idiomatic React patterns. It often produces highly type-safe and well-structured code for compound components, requiring minimal post-generation refinement. Its ability to reason about intricate type relationships makes it suitable for this advanced use case. See the full Claude hub for deeper guidance.

ChatGPT

ChatGPT provides a strong foundation for React component development and can generate functional TypeScript code. It is effective for boilerplate and common patterns, but you may need to guide it more explicitly on advanced generic inference and ensure the type safety holds up across all sub-components. Review the generated types carefully. See the full ChatGPT hub for deeper guidance.

Gemini

Gemini is capable of generating functional code quickly and can handle the basic structure of a compound component. For this task, it may require more iterative refinement to achieve the desired level of type correctness and generic sophistication, particularly in ensuring column types are consistently preserved. Verify the type definitions and their application thoroughly. See the full Gemini hub for deeper guidance.

When to use

  • When building a design system that requires a highly flexible and type-safe data table component.
  • When strong type enforcement for tabular data is critical across multiple application areas.
  • When needing to establish a consistent API for table rendering, reducing developer errors and improving maintainability.
  • When developing reusable UI components that must adapt to various data shapes while preserving TypeScript guarantees.
  • When onboarding new team members who benefit from strict type guidance in complex data display components.

When not to use

  • For simple, static tables with fixed data structures that require no generic typing or reusability.
  • When rapid prototyping where the overhead of defining complex generics outweighs the immediate benefit.
  • When working on projects that do not use TypeScript, as the core benefit of type safety is lost.
  • For one-off data displays where a simpler, less abstract component will suffice.

Get more from it

Pro tips

  • 1

    Precisely define your generic `T` type for the data shape; this prevents `any` inference and ensures robust type checking throughout the table structure.

  • 2

    Implement custom cell renderers with explicit type arguments to maintain end-to-end type safety, avoiding implicit `any` in rendering logic.

  • 3

    Utilize `React.Children.map` and `React.cloneElement` to inject necessary props or context into sub-components, streamlining composition without prop drilling.

  • 4

    Apply `React.memo` to `Table.Row` and `Table.Cell` components to optimize rendering performance, especially with large datasets and frequent updates.

  • 5

    Always provide a stable, unique `key` prop for each `Table.Row` to assist React's reconciliation, preventing rendering inconsistencies and bugs.

  • 6

    Establish clear handling for `null` or `undefined` values within `Table.Cell` renderers to prevent unexpected UI gaps or runtime errors.

  • 7

    Consider React's Context API for table-wide configurations or data access, simplifying the API for consumers and reducing prop propagation.

Don't ship this

Common mistakes

  • Not explicitly defining the generic `T` type for the table data, leading to `any` types in column access.

    Fix — Always provide a specific interface or type alias for `T` that accurately mirrors your data structure.

  • Omitting the `dataKey: keyof T` prop in `Table.Cell`, causing unsafe property access at runtime.

    Fix — Ensure `Table.Cell` receives a valid `dataKey` that is a confirmed property of the generic `T`.

  • Failing to account for `null` or `undefined` values in cell data, resulting in rendering errors or blank spaces.

    Fix — Implement explicit checks or fallback renderers within `Table.Cell` for potentially missing data values.

  • Neglecting to assign unique `key` props to `Table.Row` components, impacting list rendering performance and stability.

    Fix — Assign a stable, unique `key` to each `Table.Row`, typically using a unique identifier from the data object.

  • Custom renderer functions lacking proper type annotations for their incoming props, breaking type safety at the leaf nodes.

    Fix — Define clear TypeScript types for all props consumed by custom renderers to maintain type integrity.

  • Incorrectly iterating over `children` without `React.Children.map`, leading to broken component composition patterns.

    Fix — Use `React.Children.map` with `React.cloneElement` to correctly pass context or props to immediate child components.

People also ask

Frequently asked questions

Q.Can this component handle pagination or sorting logic?

Yes, the component provides a type-safe rendering foundation. Pagination and sorting logic are typically managed externally, either in a parent component or a custom hook, and the filtered/sorted data is then passed to the table.

Q.Is this approach suitable for very large datasets, like thousands of rows?

For very large datasets, consider integrating a virtualization library (e.g., react-window, react-virtualized) within the Table.Row rendering to optimize performance by only rendering the rows currently visible in the viewport.

Q.How do I apply custom styling or integrate with a UI library?

The prompt focuses on structure and type safety. Styling can be applied using CSS modules, Tailwind CSS, styled-components, or other methods by passing className or style props, or by composing the generated components with your UI library's primitives.

Q.What if my data has deeply nested objects that need to be displayed?

For deeply nested data, your generic T type should reflect the full structure. Table.Cell's dataKey accesses top-level properties directly. For nested access, you would typically use a custom renderer prop within Table.Cell to extract and display the specific nested value.

Q.How can I ensure this component is accessible (a11y) for all users?

Integrate appropriate ARIA roles and attributes (e.g., role="table", role="row", role="cell", aria-label) directly into the JSX structure of the generated components. This ensures they are properly interpreted by assistive technologies.

Q.Will this work with different data fetching mechanisms, like REST APIs or GraphQL?

Absolutely. The component is agnostic to the data source. You fetch your data using any mechanism, ensure it conforms to the generic T type you define, and then pass this typed data array to the Table component for rendering.

Version 1.0Last reviewed July 20, 2026
Reviewed by PromptInFlow Editorial Team