Skip to main content

How to Build a Reusable React Grid Component with TypeScript

Updated 5 min readMarkdown


Tags: React, TypeScript, JavaScript, Web Development, Enterprise Applications. React data grids are everywhere in enterprise React applications. From customer directories and order management systems to financial dashboards and audit logs, developers frequently need the same core functionality across multiple screens. The challenge is keeping these implementations consistent. Without a shared component, each screen may define its own column formatting, editing behavior, validation rules, and data-loading logic. This creates duplicated code and makes application-wide changes harder. A reusable React grid component provides a cleaner solution.

  1. Define a focused component API. Start by identifying the features that most screens need. A shared DataGrid component might expose: interface DataGridProps { columns: GridColumn[]; data: Row[]; url?: string; pageSize?: number; groupBy?: string; title?: string; onSelectionChange?: (rows: Row[]) => void; onCellEdit?: (change: CellChange) => Promise | void; } The objective is to give application screens a simple interface instead of exposing the complete configuration API of the underlying grid JavaScript Libraries.
  2. Create reusable column definitions: Define column types once and map them to the appropriate display and editor configuration. type ColumnType = | "text" | "number" | "currency" | "date" | "boolean";

interface GridColumn { field: string; header: string; type?: ColumnType; width?: number; editable?: boolean; required?: boolean; } A mapping function can translate these definitions into the grid's column configuration. For example: Number fields use number formatting and number editors. Currency fields share a consistent currency format. Date fields use a common date format and date editor. Boolean fields display Yes/No or use checkboxes. Centralizing these decisions prevents individual screens from implementing the same rules differently.

3. Keep the grid configuration stable: Avoid reconstructing the entire grid whenever new records arrive. Use React's useMemo for structural configuration and update the existing store when local data changes. This helps preserve grid state such as sorting, scroll position, and column widths. Also define reusable column arrays outside the screen component when possible. Stable references make it easier to avoid unnecessary configuration updates.

4. Standardize inline editing: Editing should behave consistently throughout the application. A useful callback structure includes: interface CellChange { row: Row; field: string; value: unknown; oldValue: unknown; } The parent component can handle the API request, while the grid wrapper controls whether the edit is committed. async function saveOrder( id: unknown, patch: Record<string, unknown> ) { const response = await fetch(/api/orders/${id}, { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify(patch), });

if (!response.ok) { throw new Error("Save failed"); } } When the operation succeeds, keep the new value. When it fails, restore the original value. This makes error handling predictable and keeps backend logic separate from grid presentation.

5. Handle local and remote data: A reusable component should support more than one data source. For example: A customer screen may receive local records through a data prop. An orders screen may use a remote URL with server-side paging, sorting, and filtering. Make sure the backend understands the parameters sent by the grid. If remote grouping is used with pagination, the API should also sort records appropriately so groups do not become fragmented across pages.

6. Reuse the component across screens. An orders page might need:

Editable customer and amount fields; date and status editors; grouping by region; summary calculations; remote pagination. A customers page might only need read-only customer details and consistent date and currency formatting. Both screens can use the same grid component. Only their column definitions and data sources need to change.

7. Consider Ext JS through ReExt: For applications with more advanced data-grid requirements, building every feature independently may not be the best use of development time. Sencha's Ext JS grid includes features such as inline editing, grouping, summaries, paging, remote sorting, and filtering. ReExt makes Ext JS components available within React applications, allowing developers to create a TypeScript-friendly wrapper around an established grid foundation. Teams with existing Ext JS applications can also reuse custom grid classes through xtype, which can support gradual modernization. The decision should account for technical requirements, existing architecture, licensing, and team expertise.

8. Test and document the component: A reusable grid becomes a shared dependency, so quality matters. Consider testing: Column mapping and formatting; required-field validation; successful edit operations; failed-save rollbacks; grouping and summaries; remote data behavior. Document the supported props, column types, and usage examples. Storybook can also help developers understand the component without reading its implementation.

Conclusion:

Building a reusable React grid component is about more than reducing repeated code. It creates consistent rules for formatting, editing, validation, and data loading across an application. Keep the API small, separate structural configuration from data updates, expose plain objects to screen components, and test the shared behavior thoroughly. For enterprise applications that need advanced grid capabilities, Ext JS with ReExt is another option worth evaluating. Original guide: How to Build a Reusable React Grid Component – Sencha

No comments yet. Be the first to comment.