๐งฉ antd-crud-table
โ A Dynamic React Table Generator with Forms ๐
antd-crud-table
is a highly flexible and powerful React library built using antd
and @ant-design/pro-components
. It provides both a declarative component-based approach and a modern hook-based architecture for creating editable, paginated tables with form support, data fetching, sorting, filtering, and custom rendering. Perfect for building admin dashboards and data management UIs with minimal boilerplate.
๐ Enhanced ๐ API Reference
CrudTableExperimental Props (Enhanced)
title | string | Table header title |
rowKey | keyof T | Unique identifier for each row |
columns | CrudColumn<T>[] | Column definitions with enhanced features |
hookConfig | UseCrudTableConfig<T> | Hook configuration for data operations |
defaultPageSize? | number | Initial page size (default: 10) |
enableBulkOperations? | boolean | Enable bulk select/delete (default: false) |
customActions? | (record, actions) => ReactNode[] | Custom row actions |
The experimental version introduces a powerful hook-based architecture with multiple data source strategies:
- Static Data: Perfect for prototypes and small datasets
- API Integration: REST API support with automatic request handling
- Custom Operations: Full control with GraphQL, IndexedDB, or custom logic
- Built-in State Management: Loading states, error handling, optimistic updates
๐ฆ Installation
npm install antd-crud-table
Peer Dependencies:
npm install react react-dom antd @ant-design/pro-components
๐ Quick Start
Choose your preferred approach:
Modern Approach (Experimental) - Hook-Based
import { CrudTableExperimental } from 'antd-crud-table';
const UserManagement = () => (
<CrudTableExperimental<User>
title="User Management"
rowKey="id"
hookConfig={{
staticData: users,
optimisticUpdates: true,
}}
columns={[
{
title: 'Name',
dataIndex: 'name',
fieldType: 'string',
formConfig: { required: true },
},
{
title: 'Status',
dataIndex: 'status',
fieldType: 'enum',
enumOptions: {
active: { text: 'Active', color: 'green' },
inactive: { text: 'Inactive', color: 'orange' },
},
},
]}
/>
);
Classic Approach (Original) - Service-Based
import { CrudTable } from 'antd-crud-table';
const userService = {
getList: async () => ({ data: [], total: 0 }),
create: async (data) => data,
update: async (id, data) => data,
delete: async (id) => {},
};
const UserTable = () => (
<CrudTable
title="User Management"
rowKey="id"
service={userService}
columns={[
{
title: 'Name',
dataIndex: 'name',
fieldType: 'string',
fieldEditable: true,
formConfig: { required: true },
},
]}
/>
);
๐ฏ Enhanced Features (Experimental)
1. Multiple Data Source Strategies
Static Data (Perfect for Prototyping)
<CrudTableExperimental
hookConfig={{
staticData: mockUsers,
optimisticUpdates: true,
}}
/>
API Integration (Production Ready)
<CrudTableExperimental
hookConfig={{
api: {
baseUrl: 'https://api.example.com',
endpoints: {
list: '/users',
create: '/users',
update: '/users',
delete: '/users',
},
headers: {
'Authorization': 'Bearer your-token',
},
transform: {
response: (data) => ({
data: data.users,
total: data.totalCount,
}),
request: (data) => ({
...data,
updatedAt: new Date().toISOString(),
}),
},
},
onSuccess: (operation, data) => {
console.log(`${operation} completed`, data);
},
onError: (operation, error) => {
console.error(`${operation} failed`, error);
},
}}
/>
Custom Operations (Maximum Flexibility)
<CrudTableExperimental
hookConfig={{
operations: {
getList: async (params) => {
const result = await myGraphQLQuery(params);
return {
data: result.users,
total: result.totalCount,
success: true,
};
},
create: async (data) => await myCreateMutation(data),
update: async (id, data) => await myUpdateMutation(id, data),
delete: async (id) => await myDeleteMutation(id),
},
}}
/>
2. Advanced Features
Bulk Operations
<CrudTableExperimental
enableBulkOperations={true}
/>
Custom Actions
<CrudTableExperimental
customActions={(record, actions) => [
<Button
key="export"
onClick={() => exportUser(record)}
>
Export
</Button>,
<Button
key="clone"
onClick={() => actions.create({...record, id: undefined})}
>
Clone
</Button>
]}
/>
Enhanced Validation
columns={[
{
dataIndex: 'email',
title: 'Email',
fieldType: 'string',
formConfig: {
required: true,
rules: [
{ required: true, message: 'Email is required' },
{ type: 'email', message: 'Invalid email format' },
{
validator: async (_, value) => {
const exists = await checkEmailExists(value);
if (exists) throw new Error('Email already exists');
}
}
],
},
},
]}
3. Custom Hooks
Create your own specialized hooks for different use cases:
Example 1: useUserCrud - Specialized User Management
import { useCrudTable, type UseCrudTableConfig } from 'antd-crud-table';
#### Example 1: useUserCrud - Specialized User Management
```tsx
import { useCrudTable, type UseCrudTableConfig } from 'antd-crud-table';
export const useUserCrud = (baseConfig?: Partial<UseCrudTableConfig<any>['api']>) => {
const config: UseCrudTableConfig<any> = {
api: {
baseUrl: '/api/users',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
transform: {
request: (data) => ({
...data,
// Add default values or transformations
updatedAt: new Date().toISOString(),
}),
response: (data) => ({
data: data.users || data.data || [],
total: data.total || data.count || 0,
success: true,
}),
},
...baseConfig,
},
defaultPageSize: 10,
optimisticUpdates: true,
onSuccess: (operation, data) => {
console.log(`User ${operation} completed:`, data);
},
onError: (operation, error) => {
console.error(`User ${operation} failed:`, error);
// Could add toast notifications, error reporting, etc.
},
};
return useCrudTable('id', config);
};
Example 2: useLocalStorageCrud - Local Storage Persistence
export const useLocalStorageCrud = <T extends Record<string, any>>(
storageKey: string,
rowKey: keyof T,
initialData: T[] = []
) => {
const getStoredData = (): T[] => {
try {
const stored = localStorage.getItem(storageKey);
return stored ? JSON.parse(stored) : initialData;
} catch {
return initialData;
}
};
const setStoredData = (data: T[]) => {
localStorage.setItem(storageKey, JSON.stringify(data));
};
return useCrudTable(rowKey, {
operations: {
getList: async (params) => {
const data = getStoredData();
const { current = 1, pageSize = 10, ...filters } = params;
let filteredData = data;
Object.entries(filters).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
filteredData = filteredData.filter(item =>
String(item[key]).toLowerCase().includes(String(value).toLowerCase())
);
}
});
const start = (current - 1) * pageSize;
const paginatedData = filteredData.slice(start, start + pageSize);
return {
data: paginatedData,
total: filteredData.length,
success: true,
};
},
create: async (newItem) => {
const data = getStoredData();
const maxId = Math.max(...data.map(item => Number(item[rowKey]) || 0), 0);
const created = {
[rowKey]: maxId + 1,
...newItem,
createdAt: new Date().toISOString(),
} as T;
data.push(created);
setStoredData(data);
return created;
},
update: async (id, updateData) => {
const data = getStoredData();
const index = data.findIndex(item => item[rowKey] === id);
if (index === -1) throw new Error('Item not found');
data[index] = {
...data[index],
...updateData,
updatedAt: new Date().toISOString(),
};
setStoredData(data);
return data[index];
},
delete: async (id) => {
const data = getStoredData();
const filtered = data.filter(item => item[rowKey] !== id);
setStoredData(filtered);
},
},
optimisticUpdates: true,
});
};
Example 3: useRealtimeCrud - WebSocket Integration
#### Example 3: useRealtimeCrud - WebSocket Integration
```tsx
export const useRealtimeCrud = <T extends Record<string, any>>(
rowKey: keyof T,
websocketUrl: string,
apiConfig: UseCrudTableConfig<T>['api']
) => {
const config: UseCrudTableConfig<T> = {
api: apiConfig,
optimisticUpdates: false, // Disable optimistic updates for realtime
};
const crud = useCrudTable(rowKey, config);
// In a real implementation, you would set up WebSocket listeners here
// useEffect(() => {
// const ws = new WebSocket(websocketUrl);
//
// ws.onmessage = (event) => {
// const { type, data } = JSON.parse(event.data);
// switch (type) {
// case 'created':
// case 'updated':
// case 'deleted':
// crud.refresh(); // Refresh data when changes occur
// break;
// }
// };
//
// return () => ws.close();
// }, [websocketUrl]);
return crud;
};
Example 4: useInfiniteScrollCrud - Infinite Scrolling
#### Example 4: useInfiniteScrollCrud - Infinite Scrolling
```tsx
export const useInfiniteScrollCrud = <T extends Record<string, any>>(
rowKey: keyof T,
baseConfig: UseCrudTableConfig<T>
) => {
// This would extend the base hook with infinite scroll capabilities
// Implementation would handle cursor-based pagination, data accumulation, etc.
const config: UseCrudTableConfig<T> = {
...baseConfig,
// Add infinite scroll specific configuration
};
return useCrudTable(rowKey, config);
};
Example 5: useCachedCrud - Advanced Caching
#### Example 5: useCachedCrud - Advanced Caching
```tsx
export const useCachedCrud = <T extends Record<string, any>>(
rowKey: keyof T,
cacheKey: string,
baseConfig: UseCrudTableConfig<T>
) => {
const config: UseCrudTableConfig<T> = {
...baseConfig,
enableCache: true,
// In a real implementation, you might integrate with:
// - React Query
// - SWR
// - Redux Toolkit Query
// - Apollo Client
// etc.
};
return useCrudTable(rowKey, config);
};
};
Usage Examples
const UserTable = () => {
const userCrud = useUserCrud();
return (
<CrudTableExperimental
title="Users"
rowKey="id"
hookConfig={userCrud}
columns={userColumns}
/>
);
};
const OfflineTable = () => {
const offlineCrud = useLocalStorageCrud<User>('users-cache', 'id', mockUsers);
return (
<CrudTableExperimental
title="Offline Users"
rowKey="id"
hookConfig={offlineCrud}
columns={userColumns}
/>
);
};
const RealtimeTable = () => {
const realtimeCrud = useRealtimeCrud<User>(
'id',
'wss://api.example.com/ws',
{ baseUrl: '/api/users' }
);
return (
<CrudTableExperimental
title="Realtime Users"
rowKey="id"
hookConfig={realtimeCrud}
columns={userColumns}
/>
);
};
๐ Complete Feature Set
Core Features
- ๐จ Multiple Column Types:
string
, number
, boolean
, date
, enum
, custom
- โ
Integrated Create/Edit Modal Forms with validation
- ๐ ProTable Integration: Sorting, pagination & filtering built-in
- ๐ Real-time Data Operations with loading states
- ๐ง Custom Transform & Render Logic per field
- ๐ Smart Date/Time Handling with
date-fns
+ dayjs
- ๐งฐ Full TypeScript Support with generics
- ๐ Field-Level Edit Controls
- ๐งผ Professional UI with row differentiation
Enhanced Features (Experimental)
- ๐ช Hook-Based Architecture with
useCrudTable
- ๐ Multiple Data Sources: Static, API, or custom operations
- โก Built-in State Management: Loading, error states, optimistic updates
- ๐ง Extensible Design: Create custom hooks for your domain
- ๐ Performance Optimized: Caching, lazy loading, optimistic updates
- ๐๏ธ Bulk Operations: Select and delete multiple rows
- ๐ฏ Custom Actions: Add your own row-level actions
- ๐ Advanced Search: Configurable column-level search
- โจ Enhanced Validation: Complex form validation rules
API Reference
CrudTableV2 Props (Enhanced)
title | string | Table header title |
rowKey | keyof T | Unique identifier for each row |
columns | CrudColumn<T>[] | Column definitions with enhanced features |
hookConfig | UseCrudTableConfig<T> | Hook configuration for data operations |
defaultPageSize? | number | Initial page size (default: 10) |
enableBulkOperations? | boolean | Enable bulk select/delete (default: false) |
customActions? | (record, actions) => ReactNode[] | Custom row actions |
CrudColumn (Enhanced)
dataIndex | keyof T | Field key in your data |
title | string | Column header text |
fieldType | FieldType | "string" | "number" | "boolean" | "date" | "enum" | "custom" |
fieldEditable? | boolean | Whether field can be edited (default: true) |
searchable? | boolean | Whether field appears in search (default: true) |
enumOptions? | Record<string, {text: string, color?: string}> | Options for enum fields |
customRender? | (value, record) => ReactNode | Custom display renderer |
formConfig? | FormConfig | Form field configuration |
FormConfig
required? | boolean | Whether field is required |
rules? | FormRule[] | Ant Design validation rules |
component? | ReactNode | Custom form component |
transform? | (value) => any | Transform value before saving |
UseCrudTableConfig
Choose one approach:
{
staticData: T[];
optimisticUpdates?: boolean;
}
{
api: {
baseUrl: string;
endpoints?: {...};
headers?: Record<string, string>;
transform?: {...};
};
}
{
operations: {
getList: (params) => Promise<{data: T[], total: number}>;
create: (data: Partial<T>) => Promise<T>;
update: (id, data: Partial<T>) => Promise<T>;
delete: (id) => Promise<void>;
};
}
Legacy CrudTable Props (Original)
columns | CrudColumn<T>[] | Column definitions |
service | CrudService<T> | Service object with CRUD methods |
rowKey | keyof T | Unique key for each row |
title | string | Table header title |
defaultPageSize? | number | Optional default page size (default: 5) |
๐ Styling
Customize row striping using .row-differentiator
in CrudTable.css
:
.row-differentiator {
background-color: #fafafa;
}
๐ Notes
- Date fields are handled via
dayjs
in the form and date-fns
for display.
- All requests are async with error handling via
antd
's message
API.
- Add your own export logic or additional toolbar buttons as needed.
๐ Migration Guide
Upgrading from Original to Experimental
Original (Service-Based):
<CrudTable
title="Users"
rowKey="id"
service={UserService}
columns={columns}
/>
Experimental (Hook-Based):
<CrudTableExperimental
title="Users"
rowKey="id"
hookConfig={{
operations: UserService,
}}
columns={columns}
/>
Breaking Changes in Experimental
- โ
Fully backward compatible: Original components still work
- ๐ New import:
CrudTableExperimental
for enhanced version
- ๐๏ธ Service โ hookConfig: More flexible configuration
- ๐ Enhanced props: Additional optional features
Lazy Loading Options
For better performance with code splitting:
import { CrudTableLazy } from 'antd-crud-table';
import { CrudTableExperimentalLazy } from 'antd-crud-table';
<CrudTableExperimentalLazy
title="Users"
rowKey="id"
hookConfig={hookConfig}
columns={columns}
/>
๐จ Column Type Examples
String Field
{
dataIndex: 'name',
title: 'Full Name',
fieldType: 'string',
formConfig: {
required: true,
rules: [
{ min: 2, message: 'Name must be at least 2 characters' }
]
},
}
Number Field
{
dataIndex: 'age',
title: 'Age',
fieldType: 'number',
formConfig: {
rules: [
{ type: 'number', min: 0, max: 120, message: 'Invalid age' }
]
},
}
Date Field
{
dataIndex: 'createdAt',
title: 'Created Date',
fieldType: 'date',
searchable: false,
}
Boolean Field
{
dataIndex: 'isActive',
title: 'Active Status',
fieldType: 'boolean',
}
Enum Field
{
dataIndex: 'status',
title: 'Status',
fieldType: 'enum',
enumOptions: {
active: { text: 'Active', color: 'green' },
pending: { text: 'Pending', color: 'orange' },
inactive: { text: 'Inactive', color: 'red' },
},
}
Custom Field
{
dataIndex: 'customField',
title: 'Custom Display',
fieldType: 'custom',
customRender: (value, record) => (
<div>
<Avatar src={record.avatar} />
<span>{record.name}</span>
</div>
),
formConfig: {
component: <MyCustomInput />,
},
}
๐งช Testing
The hook-based architecture enables easy testing:
import { renderHook, act } from '@testing-library/react';
import { useCrudTable } from 'antd-crud-table';
test('should handle CRUD operations', async () => {
const mockData = [
{ id: 1, name: 'John', age: 30 }
];
const { result } = renderHook(() =>
useCrudTable('id', {
staticData: mockData,
})
);
await act(async () => {
const created = await result.current.create({
name: 'Jane',
age: 25
});
expect(created).toBeTruthy();
});
expect(result.current.state.data).toHaveLength(2);
});
๐ Performance Tips
1. Use Static Data for Prototyping
hookConfig={{ staticData: mockData }}
2. Enable Optimistic Updates
hookConfig={{
api: {...},
optimisticUpdates: true
}}
3. Implement Proper Caching
const useUserCrud = () => {
return useCrudTable('id', {
enableCache: true,
});
};
4. Lazy Load Components
import { CrudTableLazy } from 'antd-crud-table';
๐ฎ Roadmap
Coming Soon
- ๐ WebSocket Integration: Real-time updates
- ๐ Virtual Scrolling: Handle thousands of rows
- ๐ค Export Functionality: CSV/Excel export
- ๐จ Theme Support: Multiple UI themes
- ๐ Advanced Filters: Complex filtering UI
- ๐ฑ Mobile Optimization: Better mobile experience
- ๐ง Plugin System: Extensible architecture
- ๐ Analytics Integration: Built-in tracking
- ๐ i18n Support: Multi-language support
๐ค Contributing
We welcome contributions! Please see our Contributing Guide.
Development Setup
git clone https://github.com/maifeeulasad/antd-crud-table
cd antd-crud-table
npm install
npm run dev
๐ License
MIT License - feel free to use in personal and commercial projects.
๐ Build elegant CRUD interfaces faster than ever with antd-crud-table
!
References