Date: October 2, 2025 Approach: Best of Both Worlds - Use the right tool for the right job
Don't replace what works - Keep React Admin datagrids for standard CRUD operations. Add TanStack Table - Use for advanced analytics, custom dashboards, and specialized views.
| Scenario | Why React Admin | Example |
|---|---|---|
| Standard CRUD | Built-in edit/create/delete | Contacts list page |
| Simple lists | Quick implementation | Companies directory |
| React Admin ecosystem | Tight integration with ra-core | All existing CRM views |
| Rapid prototyping | Minimal code required | Quick admin interfaces |
| Backend filters | Built-in query integration | Server-side filtering |
Code Example:
// Existing Contacts List - KEEP AS IS
<Datagrid>
<TextField source="name" />
<EmailField source="email" />
<EditButton />
</Datagrid>| Scenario | Why TanStack | Example |
|---|---|---|
| Analytics dashboards | Full customization | Revenue analytics widget |
| Complex filtering | Advanced filter UI | Multi-faceted search |
| Custom styling | Theme integration | Branded reports |
| Drag & drop | Built-in with @dnd-kit | Priority ranking |
| Virtual scrolling | Performance | Large datasets (10k+ rows) |
| Column resizing | User-configurable | Custom report views |
| Export features | Full control | CSV/PDF exports |
Code Example:
// NEW Analytics Widget - Use TanStack
import { RecentDealsWidget } from '@/components/analytics/RecentDealsWidget';
<RecentDealsWidget />/contacts → React Admin Datagrid ✅
/contacts/:id → React Admin Edit Form ✅
/companies → React Admin Datagrid ✅
/companies/:id → React Admin Edit Form ✅
/deals → React Admin Kanban ✅
/deals/:id → React Admin Edit Form ✅
Reason: These work perfectly, are stable, and users are familiar with them.
/dashboard/analytics → TanStack Table 🆕
/reports/revenue → TanStack Table 🆕
/reports/pipeline → TanStack Table 🆕
/analytics/performance → TanStack Table 🆕
Reason: Need custom styling, theme integration, and advanced features.
Use Case: Show recent deals with custom theming
Implementation:
// src/atomic-crm/dashboard/Dashboard.tsx
import { RecentDealsWidget } from '@/components/analytics/RecentDealsWidget';
export const Dashboard = () => (
<div className="space-y-6">
{/* Existing dashboard stats */}
<Welcome />
<MonthlyRevenue />
{/* NEW: TanStack Table widget */}
<RecentDealsWidget />
</div>
);Features:
- ✅ Sortable columns
- ✅ Pagination
- ✅ Theme-aware styling
- ✅ Custom badge components
- ✅ Currency formatting
Use Case: Multi-criteria search with custom UI
// src/atomic-crm/reports/AdvancedSearch.tsx
import { useReactTable } from '@tanstack/react-table';
import { DataTable } from '@/components/data-table/data-table';
import { Input } from '@/components/ui/input';
import { Select } from '@/components/ui/select';
export const AdvancedSearch = () => {
const [filters, setFilters] = useState({
dateRange: [null, null],
status: [],
valueMin: 0,
valueMax: 1000000,
});
const table = useReactTable({
data: filteredData,
columns: customColumns,
// ... full control over filtering
});
return (
<Card>
<div className="filter-panel">
{/* Custom filter UI */}
<DateRangePicker value={filters.dateRange} />
<MultiSelect value={filters.status} />
<Slider min={0} max={1000000} />
</div>
<DataTable table={table} columns={customColumns} />
</Card>
);
};| Feature | React Admin | TanStack Table |
|---|---|---|
| Edit inline | ✅ Built-in | ❌ Manual implementation |
| Delete rows | ✅ Built-in | ❌ Manual implementation |
| Create new | ✅ Built-in | ❌ Manual implementation |
| Backend integration | ✅ Automatic | |
| Custom styling | ✅ Full control | |
| Drag & drop | ❌ No | ✅ Yes (with dnd-kit) |
| Column resizing | ❌ No | ✅ Yes |
| Virtual scrolling | ❌ No | ✅ Yes (with plugin) |
| Theme integration | ✅ Perfect | |
| Export data | ✅ Full control | |
| Learning curve | ⏱️ Low | ⏱️ Medium |
TanStack tables automatically inherit your theme presets:
// Component uses shadcn/ui components which respect themes
<DataTable table={table} columns={columns} />
// When user selects "Tangerine" preset:
// → Table borders: orange
// → Primary actions: warm gold
// → Backgrounds: beige tones
// When user toggles Dark mode:
// → Same preset, dark variantReact Admin Datagrids: Harder to theme, require CSS overrides.
- Keep all existing React Admin views
- Add TanStack for new features only
- Status: ✅ Complete
- Migrate 1-2 high-traffic pages (e.g., Contacts)
- A/B test performance and UX
- Timeline: Q1 2026 (if desired)
- Replace all React Admin datagrids
- Rebuild edit/create forms
- Timeline: TBD
Recommendation: Stay in Phase 1 unless specific needs arise.
// src/atomic-crm/contacts/ContactList.tsx
import { Datagrid, List, TextField, EmailField, EditButton } from 'react-admin';
export const ContactList = () => (
<List>
<Datagrid>
<TextField source="first_name" />
<TextField source="last_name" />
<EmailField source="email" />
<EditButton />
</Datagrid>
</List>
);Pros: 5 lines of code, works perfectly. Keep it: ✅
// src/components/analytics/RecentDealsWidget.tsx
import { useReactTable } from '@tanstack/react-table';
import { DataTable } from '@/components/data-table/data-table';
export const RecentDealsWidget = () => {
const table = useReactTable({
data: deals,
columns: customColumns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});
return (
<Card className="p-6">
<h3>Recent Deals Analytics</h3>
<DataTable table={table} columns={customColumns} />
<DataTablePagination table={table} />
</Card>
);
};Pros: Full customization, theme-aware, modern UI. Use for: NEW features only.
Ask yourself:
-
Is this a standard CRUD view? → Use React Admin (faster)
-
Do I need custom styling or advanced features? → Use TanStack Table
-
Is this a dashboard widget or analytics view? → Use TanStack Table
-
Am I replacing an existing view? → Keep React Admin unless there's a strong reason to change
src/
├── atomic-crm/
│ ├── contacts/ # React Admin views ✅
│ ├── companies/ # React Admin views ✅
│ ├── deals/ # React Admin views ✅
│ └── dashboard/ # Mix of both
│ ├── Welcome.tsx # Existing
│ └── RecentDeals.tsx # NEW: TanStack widget
├── components/
│ ├── data-table/ # TanStack Table components
│ └── analytics/ # Analytics widgets (TanStack)
- Test with all 4 theme presets (Default, Tangerine, Brutalist, Soft Pop)
- Test dark/light mode toggle
- Verify sorting works
- Verify pagination works
- Test on mobile (responsive)
- Check performance with 100+ rows
- Verify export features (if implemented)
- Test keyboard navigation (accessibility)
- Open /contacts - datagrid loads
- Edit a contact - form works
- Open /companies - datagrid loads
- Open /deals - kanban works
- No console errors
// src/components/analytics/YourWidget.tsx
import { useReactTable, getCoreRowModel } from '@tanstack/react-table';
import { DataTable } from '@/components/data-table/data-table';
export const YourWidget = () => {
const data = [/* your data */];
const columns = [/* your columns */];
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return <DataTable table={table} columns={columns} />;
};// src/atomic-crm/dashboard/Dashboard.tsx
import { YourWidget } from '@/components/analytics/YourWidget';
<YourWidget />- Open http://localhost:5174/
- Verify table appears
- Test theme switching
| Component | Path | Purpose |
|---|---|---|
<DataTable> |
@/components/data-table/data-table |
Main table |
<DataTablePagination> |
@/components/data-table/data-table-pagination |
Pagination controls |
<DataTableColumnHeader> |
@/components/data-table/data-table-column-header |
Sortable headers |
<DataTableViewOptions> |
@/components/data-table/data-table-view-options |
Column visibility toggle |
<DraggableRow> |
@/components/data-table/draggable-row |
Drag & drop rows |
- Official: https://tanstack.com/table/latest
- Examples: https://tanstack.com/table/latest/docs/examples/react/basic
- See:
src/components/analytics/RecentDealsWidget.tsxfor full example - See:
src/components/data-table/for component source
Solution: Ensure you're using shadcn/ui components for cells:
// ❌ BAD
<td>{value}</td>
// ✅ GOOD
import { TableCell } from '@/components/ui/table';
<TableCell>{value}</TableCell>Solution: Ensure dnd-kit is installed and dndEnabled prop is true:
<DataTable table={table} columns={columns} dndEnabled={true} />Solution: Use pagination or virtual scrolling:
const table = useReactTable({
// ...
getPaginationRowModel: getPaginationRowModel(),
initialState: {
pagination: { pageSize: 50 },
},
});Location: src/components/analytics/RecentDealsWidget.tsx
Features: Sorting, pagination, custom badges, currency formatting
Use Case: Dashboard widget showing pipeline activity
// Planned: src/components/analytics/RevenueReport.tsx
// Features:
// - Date range filtering
// - Group by month/quarter/year
// - Export to CSV
// - Charts integration// Planned: src/components/analytics/TaskPriority.tsx
// Features:
// - Drag & drop reordering
// - Inline status updates
// - Deadline highlighting- ✅ Use TanStack for NEW dashboards and analytics
- ✅ Keep React Admin for CRUD operations
- ✅ Test themes with both light and dark modes
- ✅ Use shadcn/ui components for consistent theming
- ✅ Implement pagination for large datasets
- ✅ Use TypeScript for type safety
- ❌ Replace working React Admin views without reason
- ❌ Mix both in the same view (confusing UX)
- ❌ Skip theme testing
- ❌ Forget accessibility (keyboard nav, ARIA labels)
- ❌ Build without pagination (performance issues)
New Feature Request
↓
Is it CRUD (Create/Read/Update/Delete)?
↓ YES ↓ NO
Use React Admin Is it analytics/dashboard?
✅ ↓ YES
Use TanStack Table
✅
- ✅ No regressions in existing CRM features
- ✅ Faster development for new analytics features
- ✅ Consistent theming across all tables
- ✅ Positive user feedback on new widgets
- ✅ Maintainability remains high
Summary: Keep what works (React Admin), add what's better (TanStack) for new features. Win-win! 🎉
Questions? See example: src/components/analytics/RecentDealsWidget.tsx