File Structure
Understanding the recommended file structure for implementing the data table
File Structure
The data table follows a modular architecture that separates concerns and improves maintainability. This guide explains the recommended file organization.
Recommended Project Structure
Layer Breakdown
1. API Layer (/api)
Handles all backend communication. Each entity has its own folder with CRUD operations.
export async function fetchUsers(params: FetchUsersParams) {
// API call logic
}Key Files:
fetch-entities.ts- GET list with server-side operationsfetch-entities-by-ids.ts- GET multiple records by IDadd-entity.ts- POST create operationdelete-entity.ts- DELETE operation
2. Core Components Layer (/components/data-table)
Reusable table components used across all table implementations.
Main Components
| Component | Purpose |
|---|---|
data-table.tsx | Main table component with all logic |
column-header.tsx | Sortable column headers |
toolbar.tsx | Search, filters, and actions |
pagination.tsx | Page navigation controls |
view-options.tsx | Column visibility toggle |
data-export.tsx | CSV/Excel export UI |
expand-icon.tsx | Subrow expand/collapse icon |
Hooks
Custom React hooks for specialized functionality:
export function useTableColumnResize(tableId: string) {
// Column resize state management
}Utilities
Helper functions organized by purpose:
| Utility File | Purpose |
|---|---|
table-config.ts | Type definitions and defaults |
url-state.ts | URL state persistence |
export-utils.ts | Data export transformations |
search.ts | Search functionality |
date-format.ts | Date formatting utilities |
column-sizing.ts | Column width calculations |
3. Implementation Layer (/app/(section)/entity-table)
Entity-specific table implementations. This is where you customize the table for your specific data.
Components Directory
columns.tsx - Define your table columns:
export const getColumns = (handleRowDeselection) => [
{
accessorKey: "name",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Name" />
),
// ...
},
];row-actions.tsx - Actions for individual rows:
export function DataTableRowActions({ row, table }) {
// Edit, delete, view details, etc.
}toolbar-options.tsx - Custom toolbar content:
export function ToolbarOptions({ selectedRows, resetSelection }) {
// Add, bulk delete, bulk actions, etc.
}Schema Directory
Type definitions and validation schemas:
export const entitySchema = z.object({
id: z.number(),
name: z.string(),
// ...
});
export type Entity = z.infer<typeof entitySchema>;Utils Directory
config.ts - Export configuration:
export function useExportConfig() {
return {
columnMapping: { /* ... */ },
headers: [/* ... */],
entityName: "entities",
};
}data-fetching.ts - React Query hooks:
export function useEntitiesData(/* params */) {
return useQuery({
queryKey: ["entities", /* ... */],
queryFn: () => fetchEntities(/* ... */),
});
}Main Files
index.tsx - Table component wrapper:
export default function EntityTable() {
return (
<DataTable
getColumns={getColumns}
fetchDataFn={useEntitiesData}
// ... other props
/>
);
}page.tsx - Next.js page component:
export default function EntityPage() {
return <EntityTable />;
}File Organization Principles
Separation of Concerns
Each layer has a specific responsibility:
- API Layer - Backend communication only
- Core Components - Reusable UI and logic
- Implementation - Entity-specific configuration
- Schema - Type safety and validation
- Utils - Helper functions and configuration
Modularity
Components are self-contained and reusable:
// ✅ Good - Modular and reusable
import { DataTable } from "@/components/data-table/data-table";
import { getColumns } from "./components/columns";
// ❌ Bad - Tightly coupled
import { CustomDataTableWithEverything } from "./monster-component";Discoverability
Files are named clearly and organized logically:
✅ Good Structure:
❌ Poor Structure:
Creating a New Table
Follow this pattern to create a new table:
Create the directory structure
mkdir -p src/app/(section)/entity-table/{components/actions,schema,utils}Add API functions
Create CRUD operations in /api/entity/.
Define schemas
Add Zod schemas in schema/index.ts.
Create column definitions
Implement columns in components/columns.tsx.
Add data fetching hook
Create React Query hook in utils/data-fetching.ts.
Implement the table
Wire everything together in index.tsx.
Create the page
Add the Next.js page in page.tsx.
Best Practices
Naming Conventions
- Components: PascalCase (e.g.,
DataTable.tsx) - Utilities: kebab-case (e.g.,
url-state.ts) - Hooks: camelCase with 'use' prefix (e.g.,
useTableState) - Types: PascalCase (e.g.,
User,TableConfig)
Import Organization
// 1. External libraries
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
// 2. Internal components
import { DataTable } from "@/components/data-table/data-table";
import { Button } from "@/components/ui/button";
// 3. Local files
import { getColumns } from "./components/columns";
import { User } from "./schema";File Size
Keep files focused and manageable:
- Components: < 200 lines
- Utilities: < 150 lines
- Configuration: < 100 lines
If a file grows too large, split it into smaller modules.
Next Steps
- Quick Start Guide - Build your first table
- Core Concepts - Understand the architecture
- Examples - See real implementations
How is this guide?