React & Composition Best Practices
Relevant source files
The following files were used as context for generating this wiki page:
React & Composition Best Practices within the Better Tables project focus on maintaining a clean boundary between core logic, database adapters, and the UI layer. The project leverages modern React patterns, specifically targeting React 19+, to ensure end-to-end type safety and optimal performance in data-heavy applications.
The architectural core of these practices involves a "shadcn-style" distribution model where UI components are copied directly into consumer projects. This approach allows developers to own the source code and customize components while maintaining the robust data-management capabilities provided by @better-tables/core. Key focus areas include efficient state management through specialized hooks, stable dependency management in effects, and the use of compositional patterns for extensible cell rendering.
Sources: README.md:6, README.md:104-110, CLAUDE.md:12-25, plans/034-usetabledata-loop-and-date-render.md:13-16
UI Architecture and Distribution
The project follows a specific distribution and composition model for its React components. Unlike traditional npm-published UI libraries, @better-tables/ui is marked as private and is distributed via a CLI tool.
Shadcn-style Component Distribution
The UI components, hooks, and stores are designed to be copied into the consumer's components/ directory. This allows the project to provide production-ready components that remain fully customizable. The CLI init command handles this process, ensuring that imports are rewritten to match the project's local aliases.
Sources: README.md:104-114, CLAUDE.md:65-72, plans/009-dx-hygiene-sweep.md:162-175
Client-Side Boundaries
Because the library targets Next.js and other modern frameworks, strict management of "use client" directives is maintained. The build system (using tsdown) is configured to preserve per-file client directives to ensure proper React Server Component (RSC) compatibility. This prevents the entire UI package from becoming a single monolithic client boundary.
Sources: plans/009-dx-hygiene-sweep.md:67-83, CLAUDE.md:67-72
Component Composition Patterns
Better Tables utilizes several composition patterns to handle complex data rendering and interaction.
Cell Rendering and Wrappers
The library uses a compositional approach to render table cells. Standard formatters are used by default, but developers can provide custom cellRenderer functions. For features like inline editing, the library uses a wrapper pattern where the existing display output is wrapped in an EditableCell component.
The diagram shows the decision logic for rendering a cell, incorporating both custom renderers and the editable wrapper logic.
Sources: plans/053-editable-cells.md:46-52, plans/053-editable-cells.md:144-149
Editable Cell UX
Inline editing is implemented with specific triggers to avoid conflicts with other row-level interactions (like selection or navigation):
- Triggers: Double-click or the Enter key opens the editor.
- Commit: Enter (or blur) saves the value.
- Cancel: Escape restores the original display.
Sources: plans/053-editable-cells.md:158-166
Hook Stability and Performance
A critical aspect of React best practices in this project is ensuring hook stability to prevent infinite re-render loops and unnecessary refetches.
Stable Dependency Defaults
Hooks like useTableData must avoid using inline object or array literals as default parameters (e.g., filters = []). These create fresh references on every render, triggering effects that depend on them. The project mandates hoisting these to module-level constants.
| Constant | Type | Purpose |
|---|---|---|
EMPTY_FILTERS | FilterState[] | Prevents useCallback recreation for filters. |
EMPTY_PARAMS | Record<string, unknown> | Prevents fetch effect re-triggers for params. |
Sources: plans/034-usetabledata-loop-and-date-render.md:13-33, plans/034-usetabledata-loop-and-date-render.md:74-85
State Management for Mutations
For complex interactions like cell editing, the project utilizes a specialized hook (useEditableCells) to manage optimistic updates and rollbacks.
This sequence diagram illustrates the optimistic update pattern and the rollback mechanism used during cell edits.
Sources: plans/053-editable-cells.md:112-126
Data Rendering and Formatting
Consistency in data representation is maintained through centralized utility functions that handle various data types, including those received from JSON transports.
Date Coercion
A key best practice is ensuring that dates received as ISO strings or numbers (common in HTTP-based communication) are correctly coerced into Date objects before formatting. This ensures that the UI correctly respects the column's configured dateFormat and timezone settings.
Sources: plans/034-usetabledata-loop-and-date-render.md:115-127
Formatting Logic
The getFormatterForType utility serves as the central point for data formatting, ensuring that date, number, currency, and boolean types are rendered consistently across standard and virtualized tables.
Sources: plans/034-usetabledata-loop-and-date-render.md:46-52
Summary of Configuration Options
The following table summarizes key React-related configuration options available in the UI layer:
| Prop/Option | Type | Description |
|---|---|---|
cellRenderer | Function | Custom React node generator for a specific column. |
editRenderer | Function | Escape hatch for providing a custom editor component. |
onCellEdit | Function | Callback for manual persistence (overrides adapter path). |
virtualized | boolean | Enables high-performance rendering for large datasets. |
initialFilters | FilterState[] | Initial filter state passed to the table. |
Sources: plans/053-editable-cells.md:95-110, plans/057-contract-surface-truth.md:72-80
The React and Composition practices in Better Tables prioritize developer experience (DX) by combining the power of a centralized core with the flexibility of locally-owned UI components, all while enforcing strict patterns for performance and stability.