TNKS Data Table

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.

add-entity.ts
delete-entity.ts
fetch-entities.ts
fetch-entity-by-ids.ts
use-table-column-resize.ts
column-sizing.ts
conditional-state.ts
date-format.ts
deep-utils.ts
export-utils.ts
keyboard-navigation.ts
search.ts
table-config.ts
table-state-handlers.ts
url-state.ts
column-header.tsx
data-export.tsx
data-table-resizer.tsx
data-table.tsx
expand-icon.tsx
pagination.tsx
subrow-columns.tsx
toolbar.tsx
view-options.tsx
calendar-date-picker.tsx
columns.tsx
row-actions.tsx
toolbar-options.tsx
add-entity-popup.tsx
delete-entity-popup.tsx
bulk-delete-popup.tsx
index.ts
config.ts
data-fetching.ts
index.tsx
page.tsx
utils.ts
providers.tsx

Layer Breakdown

1. API Layer (/api)

Handles all backend communication. Each entity has its own folder with CRUD operations.

src/api/user/fetch-users.ts
export async function fetchUsers(params: FetchUsersParams) {
  // API call logic
}

Key Files:

  • fetch-entities.ts - GET list with server-side operations
  • fetch-entities-by-ids.ts - GET multiple records by ID
  • add-entity.ts - POST create operation
  • delete-entity.ts - DELETE operation

2. Core Components Layer (/components/data-table)

Reusable table components used across all table implementations.

Main Components

ComponentPurpose
data-table.tsxMain table component with all logic
column-header.tsxSortable column headers
toolbar.tsxSearch, filters, and actions
pagination.tsxPage navigation controls
view-options.tsxColumn visibility toggle
data-export.tsxCSV/Excel export UI
expand-icon.tsxSubrow expand/collapse icon

Hooks

Custom React hooks for specialized functionality:

src/components/data-table/hooks/use-table-column-resize.ts
export function useTableColumnResize(tableId: string) {
  // Column resize state management
}

Utilities

Helper functions organized by purpose:

Utility FilePurpose
table-config.tsType definitions and defaults
url-state.tsURL state persistence
export-utils.tsData export transformations
search.tsSearch functionality
date-format.tsDate formatting utilities
column-sizing.tsColumn 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
row-actions.tsx
toolbar-options.tsx
add-entity-popup.tsx
delete-entity-popup.tsx
bulk-delete-popup.tsx

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:

schema/index.ts
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:

  1. API Layer - Backend communication only
  2. Core Components - Reusable UI and logic
  3. Implementation - Entity-specific configuration
  4. Schema - Type safety and validation
  5. 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:

columns.tsx
row-actions.tsx
index.ts
config.ts
data-fetching.ts

❌ Poor Structure:

stuff.tsx
helpers.ts
things.ts

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

How is this guide?