GridView Component - CRUD Operations ✨
Complete data grid with Create, Read, Update, Delete operations. Powered by JSONPlaceholder API for demo purposes.
Key Features
- Full CRUD: Create, Read, Update, Delete operations
- Inline Editing: Edit rows directly in modal forms
- Auto Refresh: Built-in refresh button
- Validation: Form validation with Yup
- Type Safe: Full TypeScript support
- Nested Objects: Support for complex data structures
- Responsive: Mobile-friendly design
- Loading States: Skeleton loaders and spinners
Demo API: JSONPlaceholder
This demo uses JSONPlaceholder, a free fake REST API for testing and prototyping.
📋 Note: JSONPlaceholder is a mock API, so:
- Create: Will simulate creation but won't persist
- Update: Will simulate update but won't save
- Delete: Will simulate deletion but won't remove
- Changes are visible locally until you refresh the page
Live Demo
Try adding, editing, and deleting users. Click the Add User button to create a new user, click the edit icon to modify, or delete icon to remove.
Users
All Users
🚀 Direct External API Demo (No Proxy!)
This GridView fetches data directly from https://jsonplaceholder.typicode.com/users without any proxy endpoint. Notice we don't use responseProp because the API returns an array directly.
Users (Direct API)
All Users (Direct API)
Code for this example:
<GridView<User>
name="User"
names="Users"
apiUrl="https://jsonplaceholder.typicode.com/users"
fields={userFields}
idFieldName="id"
showHeader={true}
showRefresh={true}
// NO responseProp! API returns array directly: [...]
/>
// Read-only mode - no add/edit/delete buttons shown
// For CRUD operations, you need proxy endpointsHow to Build This
Step 1: Define Your Data Type
Step 2: Define Fields Configuration
Step 3: Render GridView Component
Complete Working Example
API Response Format
GridView expects specific response formats from your API endpoints:
GET /api/users (List)
{
"success": true,
"data": [
{ "id": 1, "name": "John", "email": "john@example.com" },
{ "id": 2, "name": "Jane", "email": "jane@example.com" }
]
}
// Use responseProp="data" to extract the arrayGET /api/users/:id (Single Item - for Edit)
{
"success": true,
"data": { "id": 1, "name": "John", "email": "john@example.com" }
}
// Use editResponseProp="data" to extract the objectPOST /api/users (Create)
// Response
{
"success": true,
"data": { "id": 3, "name": "New User", "email": "new@example.com" },
"message": "User created successfully"
}PUT /api/users/:id (Update)
// Response
{
"success": true,
"data": { "id": 1, "name": "Updated Name", "email": "updated@example.com" },
"message": "User updated successfully"
}DELETE /api/users/:id (Delete)
// Response
{
"success": true,
"message": "User deleted successfully"
}GridView Props
| Prop | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Singular name (e.g., "User") |
| names | string | No | Plural name (e.g., "Users") |
| apiUrl | string | Yes | API endpoint to fetch data |
| fields | Field<T> | Yes | Field definitions for grid columns |
| idFieldName | keyof T | No | ID field name (default: "id") |
| addApiUrl | string | No | API endpoint for creating items |
| editApiUrl | string | No | API endpoint for updating items (use $<row.id> or [id] placeholder) |
| deleteApiUrl | string | No | API endpoint for deleting items (use $<row.id> or [id] placeholder) |
| showHeader | boolean | No | Show grid header (default: true) |
| showRefresh | boolean | No | Show refresh button (default: false) |
| responseProp | string | No | Property to extract array from API response (e.g., "data") |
| editResponseProp | string | No | Property to extract object from edit API response (e.g., "data") |
| refetchOnEdit | boolean | No | Refetch data before editing (default: false) |
| autoRefresh | boolean | No | Auto-refresh data periodically |
Using External APIs Directly (No Proxy Needed!)
You can use external APIs like JSONPlaceholder directly without creating proxy endpoints. Just omit responseProp if the API returns an array directly, or use it to extract the data from a wrapper object.
Example 1: API Returns Array Directly
When the API returns [{...}, {...}] directly (like JSONPlaceholder):
// JSONPlaceholder returns: [{ id: 1, name: "..." }, ...]
// DON'T use responseProp!
<GridView<User>
name="User"
names="Users"
apiUrl="https://jsonplaceholder.typicode.com/users"
fields={userFields}
idFieldName="id"
showHeader={true}
showRefresh={true}
// Note: No responseProp! API returns array directly
/>
// Read-only mode (no CRUD operations)
// If you want to enable CRUD, you'll need proxy endpointsExample 2: API Returns Wrapped Response
When the API returns {success: true, data: [{...}, ...]}:
// API returns: { success: true, data: [...] }
// USE responseProp="data"
<GridView<User>
name="User"
names="Users"
apiUrl="/api/users"
fields={userFields}
idFieldName="id"
responseProp="data" // Extract array from response.data
showHeader={true}
showRefresh={true}
/>Example 3: Nested Data with objectProp
When each item has a nested object you want to extract:
// API returns: { data: [{ id: 1, user: { name: "...", email: "..." } }] }
// Extract 'user' from each item
<GridView<User>
name="User"
names="Users"
apiUrl="/api/users"
fields={userFields}
idFieldName="id"
responseProp="data" // Extract array from response.data
objectProp="user" // Extract 'user' object from each item
showHeader={true}
/>Quick Reference: When to Use What
| API Response Format | Props to Use |
|---|---|
| [{...}, {...}] | No responseProp needed |
| {data: [{...}]} | responseProp="data" |
| {result: [{...}]} | responseProp="result" |
| [{id: 1, user: {...}}] | objectProp="user" |
⚠️ Important Notes:
- CORS: External APIs must allow CORS from your domain
- Read-Only: Direct external API usage is typically read-only
- CRUD Operations: For Create/Update/Delete, you need proxy endpoints
- Authentication: API keys/tokens should be in proxy endpoints, not client-side
- Rate Limits: Be aware of API rate limiting
Advanced Features
Custom Row Actions
Add custom buttons/actions to each row:
<GridView
customActions={(item, fetchData) => (
<button onClick={() => handleCustomAction(item)}>
Custom Action
</button>
)}
/>Nested Objects
Handle complex nested data structures:
company: {
name: 'company',
label: 'Company',
type: 'object',
fields: {
name: {
name: 'name',
label: 'Company Name',
type: 'text',
},
address: {
name: 'address',
label: 'Address',
type: 'text',
},
},
}Row Click Handling
Handle clicks on rows:
<GridView
onRowClick={(e, item, setDetailedView) => {
console.log('Clicked row:', item);
// Navigate or show details
}}
/>