0
# Advanced Features
1
2
React Admin provides powerful advanced features including data export functionality, application preferences system, persistent stores, caching strategies, and comprehensive utilities for building sophisticated admin applications.
3
4
## Export Functionality
5
6
### Export System Overview
7
8
React Admin provides built-in data export capabilities with customizable formats and processing.
9
10
```typescript { .api }
11
import { Exporter } from 'react-admin';
12
13
type Exporter = (
14
data: any[],
15
fetchRelatedRecords: FetchRelatedRecords,
16
dataProvider: DataProvider,
17
resource?: string
18
) => void | Promise<void>;
19
20
type FetchRelatedRecords = (
21
data: any[],
22
field: string,
23
resource: string
24
) => Promise<any[]>;
25
```
26
27
### useExporter Hook
28
29
Hook for accessing export functionality in custom components.
30
31
```typescript { .api }
32
import { useExporter } from 'react-admin';
33
34
const useExporter: () => {
35
exporter: Exporter;
36
loading: boolean;
37
error: any;
38
};
39
```
40
41
### ExportButton
42
43
Pre-built button for triggering data export.
44
45
```typescript { .api }
46
import { ExportButton } from 'react-admin';
47
48
interface ExportButtonProps {
49
disabled?: boolean;
50
exporter?: Exporter;
51
icon?: React.ReactElement;
52
label?: string;
53
maxResults?: number;
54
resource?: string;
55
sort?: SortPayload;
56
filter?: any;
57
className?: string;
58
sx?: any;
59
}
60
61
const ExportButton: React.FC<ExportButtonProps>;
62
```
63
64
### Built-in Export Functions
65
66
```typescript { .api }
67
import {
68
defaultExporter,
69
downloadCSV,
70
fetchRelatedRecords
71
} from 'react-admin';
72
73
const defaultExporter: Exporter;
74
75
const downloadCSV: (data: any[], filename?: string) => void;
76
77
const fetchRelatedRecords: FetchRelatedRecords;
78
```
79
80
### Export Examples
81
82
```typescript
83
import {
84
List,
85
Datagrid,
86
TextField,
87
ExportButton,
88
TopToolbar,
89
downloadCSV,
90
fetchRelatedRecords,
91
useDataProvider
92
} from 'react-admin';
93
94
// Basic export with default CSV exporter
95
const PostListActions = () => (
96
<TopToolbar>
97
<ExportButton />
98
</TopToolbar>
99
);
100
101
// Custom exporter with related data
102
const customPostExporter = async (posts, fetchRelatedRecords, dataProvider) => {
103
// Fetch related categories and authors
104
const postsWithCategories = await fetchRelatedRecords(posts, 'categoryId', 'categories');
105
const postsWithAuthors = await fetchRelatedRecords(postsWithCategories, 'authorId', 'users');
106
107
// Transform data for export
108
const exportData = postsWithAuthors.map(post => ({
109
id: post.id,
110
title: post.title,
111
status: post.status,
112
category: post.category?.name || 'Unknown',
113
author: post.author ? `${post.author.firstName} ${post.author.lastName}` : 'Unknown',
114
createdAt: new Date(post.createdAt).toLocaleDateString(),
115
wordCount: post.content?.split(' ').length || 0
116
}));
117
118
downloadCSV(exportData, 'posts-detailed');
119
};
120
121
const PostList = () => (
122
<List actions={<PostListActions />} exporter={customPostExporter}>
123
<Datagrid>
124
<TextField source="title" />
125
<TextField source="status" />
126
<DateField source="createdAt" />
127
</Datagrid>
128
</List>
129
);
130
131
// Excel export example
132
import * as XLSX from 'xlsx';
133
134
const excelExporter = (data) => {
135
const worksheet = XLSX.utils.json_to_sheet(data);
136
const workbook = XLSX.utils.book_new();
137
XLSX.utils.book_append_sheet(workbook, worksheet, 'Posts');
138
XLSX.writeFile(workbook, 'posts.xlsx');
139
};
140
141
// PDF export example
142
import jsPDF from 'jspdf';
143
import 'jspdf-autotable';
144
145
const pdfExporter = (data) => {
146
const doc = new jsPDF();
147
148
doc.text('Posts Report', 20, 10);
149
150
const tableData = data.map(post => [
151
post.id,
152
post.title,
153
post.status,
154
new Date(post.createdAt).toLocaleDateString()
155
]);
156
157
doc.autoTable({
158
head: [['ID', 'Title', 'Status', 'Created']],
159
body: tableData,
160
startY: 20
161
});
162
163
doc.save('posts.pdf');
164
};
165
```
166
167
## Preferences System
168
169
### Preference Hooks
170
171
React Admin provides a preferences system for storing user interface customizations.
172
173
```typescript { .api }
174
import { usePreference } from 'react-admin';
175
176
interface UsePreferenceResult<T = any> {
177
0: T;
178
1: (value: T) => void;
179
identity: T;
180
setValue: (value: T) => void;
181
}
182
183
const usePreference: <T = any>(
184
key: string,
185
defaultValue?: T
186
) => UsePreferenceResult<T>;
187
```
188
189
### Preferences Editor
190
191
```typescript { .api }
192
import {
193
usePreferencesEditor,
194
PreferencesEditorContext,
195
PreferencesEditorContextProvider
196
} from 'react-admin';
197
198
interface PreferencesEditorContextValue {
199
isEnabled: boolean;
200
enable: () => void;
201
disable: () => void;
202
preferenceKey: string;
203
setPreferenceKey: (key: string) => void;
204
}
205
206
const usePreferencesEditor: () => PreferencesEditorContextValue;
207
```
208
209
### Configurable Components
210
211
```typescript { .api }
212
import { Configurable } from 'react-admin';
213
214
interface ConfigurableProps {
215
children: React.ReactNode;
216
editor?: React.ComponentType<ConfigurableEditorProps>;
217
preferenceKey?: string;
218
sx?: any;
219
}
220
221
const Configurable: React.FC<ConfigurableProps>;
222
```
223
224
### Preference Examples
225
226
```typescript
227
import {
228
usePreference,
229
Configurable,
230
List,
231
Datagrid,
232
TextField,
233
BooleanField
234
} from 'react-admin';
235
236
// Custom component with preferences
237
const CustomizableDashboard = () => {
238
const [showStats, setShowStats] = usePreference('dashboard.showStats', true);
239
const [refreshInterval, setRefreshInterval] = usePreference('dashboard.refreshInterval', 30000);
240
const [layout, setLayout] = usePreference('dashboard.layout', 'grid');
241
242
return (
243
<Configurable preferenceKey="dashboard">
244
<div>
245
<div>
246
<label>
247
<input
248
type="checkbox"
249
checked={showStats}
250
onChange={(e) => setShowStats(e.target.checked)}
251
/>
252
Show Statistics
253
</label>
254
</div>
255
256
<div>
257
<label>
258
Refresh Interval:
259
<select
260
value={refreshInterval}
261
onChange={(e) => setRefreshInterval(Number(e.target.value))}
262
>
263
<option value={15000}>15 seconds</option>
264
<option value={30000}>30 seconds</option>
265
<option value={60000}>1 minute</option>
266
</select>
267
</label>
268
</div>
269
270
{showStats && <StatsWidget refreshInterval={refreshInterval} />}
271
272
<div className={`layout-${layout}`}>
273
<MainContent />
274
</div>
275
</div>
276
</Configurable>
277
);
278
};
279
280
// Configurable field visibility
281
const ConfigurablePostList = () => {
282
const [showId, setShowId] = usePreference('postList.showId', false);
283
const [showDate, setShowDate] = usePreference('postList.showDate', true);
284
285
return (
286
<List>
287
<Datagrid>
288
{showId && <TextField source="id" />}
289
<TextField source="title" />
290
<TextField source="status" />
291
{showDate && <DateField source="createdAt" />}
292
</Datagrid>
293
</List>
294
);
295
};
296
```
297
298
## Store Management
299
300
### Store Interface
301
302
React Admin provides persistent storage for application state.
303
304
```typescript { .api }
305
import { Store } from 'react-admin';
306
307
interface Store {
308
getItem: (key: string) => any;
309
setItem: (key: string, value: any) => void;
310
removeItem: (key: string) => void;
311
removeItems: (keys: string[]) => void;
312
reset: () => void;
313
}
314
```
315
316
### Built-in Store Implementations
317
318
```typescript { .api }
319
import { localStorageStore, memoryStore } from 'react-admin';
320
321
const localStorageStore: Store;
322
const memoryStore: Store;
323
```
324
325
### Store Hooks
326
327
```typescript { .api }
328
import {
329
useStore,
330
useStoreContext,
331
useRemoveFromStore,
332
useRemoveItemsFromStore,
333
useResetStore
334
} from 'react-admin';
335
336
const useStore: <T = any>(key: string, defaultValue?: T) => [T, (value: T) => void];
337
const useStoreContext: () => Store;
338
const useRemoveFromStore: () => (key: string) => void;
339
const useRemoveItemsFromStore: () => (keys: string[]) => void;
340
const useResetStore: () => () => void;
341
```
342
343
### Custom Store Implementation
344
345
```typescript
346
import { Store } from 'react-admin';
347
348
// Custom store with encryption
349
class EncryptedLocalStorageStore implements Store {
350
private encrypt(value: any): string {
351
return btoa(JSON.stringify(value));
352
}
353
354
private decrypt(encrypted: string): any {
355
try {
356
return JSON.parse(atob(encrypted));
357
} catch {
358
return null;
359
}
360
}
361
362
getItem(key: string): any {
363
const encrypted = localStorage.getItem(key);
364
return encrypted ? this.decrypt(encrypted) : null;
365
}
366
367
setItem(key: string, value: any): void {
368
localStorage.setItem(key, this.encrypt(value));
369
}
370
371
removeItem(key: string): void {
372
localStorage.removeItem(key);
373
}
374
375
removeItems(keys: string[]): void {
376
keys.forEach(key => localStorage.removeItem(key));
377
}
378
379
reset(): void {
380
localStorage.clear();
381
}
382
}
383
384
const encryptedStore = new EncryptedLocalStorageStore();
385
386
// Usage in Admin
387
<Admin store={encryptedStore} dataProvider={dataProvider}>
388
<Resource name="posts" list={PostList} />
389
</Admin>
390
```
391
392
### Store Usage Examples
393
394
```typescript
395
import { useStore, useResetStore } from 'react-admin';
396
397
const UserPreferences = () => {
398
const [theme, setTheme] = useStore('user.theme', 'light');
399
const [sidebar, setSidebar] = useStore('user.sidebarCollapsed', false);
400
const [language, setLanguage] = useStore('user.language', 'en');
401
const resetStore = useResetStore();
402
403
return (
404
<div>
405
<h2>User Preferences</h2>
406
407
<div>
408
<label>
409
Theme:
410
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
411
<option value="light">Light</option>
412
<option value="dark">Dark</option>
413
</select>
414
</label>
415
</div>
416
417
<div>
418
<label>
419
<input
420
type="checkbox"
421
checked={sidebar}
422
onChange={(e) => setSidebar(e.target.checked)}
423
/>
424
Collapse Sidebar
425
</label>
426
</div>
427
428
<button onClick={resetStore}>
429
Reset All Preferences
430
</button>
431
</div>
432
);
433
};
434
435
// Persistent form draft
436
const DraftManager = () => {
437
const [draft, setDraft] = useStore('form.postDraft', {});
438
439
const saveDraft = (formData) => {
440
setDraft({
441
...formData,
442
savedAt: new Date().toISOString()
443
});
444
};
445
446
const clearDraft = () => {
447
setDraft({});
448
};
449
450
return { draft, saveDraft, clearDraft };
451
};
452
```
453
454
## Application Update Management
455
456
### Update Detection and Notification
457
458
```typescript { .api }
459
import {
460
useCheckForApplicationUpdate,
461
CheckForApplicationUpdate,
462
ApplicationUpdatedNotification
463
} from 'react-admin';
464
465
const useCheckForApplicationUpdate: () => {
466
updateAvailable: boolean;
467
checkForUpdate: () => void;
468
};
469
470
const CheckForApplicationUpdate: React.FC<{
471
interval?: number;
472
url?: string;
473
disabled?: boolean;
474
}>;
475
476
const ApplicationUpdatedNotification: React.FC;
477
```
478
479
### Update Management Example
480
481
```typescript
482
import {
483
Layout,
484
CheckForApplicationUpdate,
485
ApplicationUpdatedNotification
486
} from 'react-admin';
487
488
const CustomLayout = ({ children, ...props }) => (
489
<>
490
<Layout {...props}>
491
{children}
492
</Layout>
493
<CheckForApplicationUpdate interval={60000} />
494
<ApplicationUpdatedNotification />
495
</>
496
);
497
498
// Manual update checking
499
const UpdateChecker = () => {
500
const { updateAvailable, checkForUpdate } = useCheckForApplicationUpdate();
501
502
return (
503
<div>
504
<button onClick={checkForUpdate}>
505
Check for Updates
506
</button>
507
{updateAvailable && (
508
<div style={{ color: 'orange' }}>
509
Update available! Please refresh the page.
510
</div>
511
)}
512
</div>
513
);
514
};
515
```
516
517
## Utility Functions and Helpers
518
519
### Data Utilities
520
521
```typescript { .api }
522
import {
523
removeEmpty,
524
removeKey,
525
getMutationMode,
526
linkToRecord,
527
escapePath
528
} from 'react-admin';
529
530
const removeEmpty: (object: any) => any;
531
const removeKey: (object: any, key: string) => any;
532
const getMutationMode: () => 'pessimistic' | 'optimistic' | 'undoable';
533
const linkToRecord: (basePath: string, id: Identifier, type?: string) => string;
534
const escapePath: (path: string) => string;
535
```
536
537
### Async Utilities
538
539
```typescript { .api }
540
import { asyncDebounce } from 'react-admin';
541
542
const asyncDebounce: <T extends (...args: any[]) => Promise<any>>(
543
func: T,
544
delay: number
545
) => T;
546
```
547
548
### React Utilities
549
550
```typescript { .api }
551
import { mergeRefs, shallowEqual } from 'react-admin';
552
553
const mergeRefs: <T = any>(...refs: React.Ref<T>[]) => React.RefCallback<T>;
554
const shallowEqual: (a: any, b: any) => boolean;
555
```
556
557
### Development Utilities
558
559
```typescript { .api }
560
import { useWhyDidYouUpdate, useEvent } from 'react-admin';
561
562
const useWhyDidYouUpdate: (name: string, props: Record<string, any>) => void;
563
const useEvent: <T extends (...args: any[]) => any>(handler: T) => T;
564
```
565
566
## Advanced Integration Examples
567
568
### Custom Data Pipeline
569
570
```typescript
571
import {
572
useDataProvider,
573
useStore,
574
useNotify,
575
asyncDebounce
576
} from 'react-admin';
577
578
const useAdvancedDataManager = () => {
579
const dataProvider = useDataProvider();
580
const [cache, setCache] = useStore('dataCache', {});
581
const notify = useNotify();
582
583
// Debounced search function
584
const debouncedSearch = asyncDebounce(async (query: string) => {
585
try {
586
const { data } = await dataProvider.getList('posts', {
587
pagination: { page: 1, perPage: 10 },
588
sort: { field: 'score', order: 'DESC' },
589
filter: { q: query }
590
});
591
592
return data;
593
} catch (error) {
594
notify('Search failed', { type: 'error' });
595
return [];
596
}
597
}, 300);
598
599
// Cached data fetcher
600
const getCachedData = async (resource: string, id: string) => {
601
const cacheKey = `${resource}:${id}`;
602
603
if (cache[cacheKey]) {
604
return cache[cacheKey];
605
}
606
607
try {
608
const { data } = await dataProvider.getOne(resource, { id });
609
setCache({ ...cache, [cacheKey]: data });
610
return data;
611
} catch (error) {
612
notify(`Failed to fetch ${resource}`, { type: 'error' });
613
return null;
614
}
615
};
616
617
return {
618
search: debouncedSearch,
619
getCachedData,
620
clearCache: () => setCache({})
621
};
622
};
623
```
624
625
### Analytics Integration
626
627
```typescript
628
import { useStore, useDataProvider } from 'react-admin';
629
630
const useAnalytics = () => {
631
const [events, setEvents] = useStore('analytics.events', []);
632
const dataProvider = useDataProvider();
633
634
const trackEvent = (eventType: string, data: any) => {
635
const event = {
636
type: eventType,
637
data,
638
timestamp: new Date().toISOString(),
639
userId: getCurrentUserId()
640
};
641
642
setEvents([...events, event]);
643
644
// Send to analytics service
645
sendToAnalytics(event);
646
};
647
648
const trackPageView = (resource: string, action: string) => {
649
trackEvent('page_view', { resource, action });
650
};
651
652
const trackUserAction = (action: string, resource: string, recordId?: string) => {
653
trackEvent('user_action', { action, resource, recordId });
654
};
655
656
return {
657
trackEvent,
658
trackPageView,
659
trackUserAction,
660
events
661
};
662
};
663
664
// Usage in components
665
const AnalyticsWrapper = ({ children, resource, action }) => {
666
const { trackPageView } = useAnalytics();
667
668
useEffect(() => {
669
trackPageView(resource, action);
670
}, [resource, action]);
671
672
return children;
673
};
674
```
675
676
### Performance Monitoring
677
678
```typescript
679
import { useWhyDidYouUpdate, useEvent } from 'react-admin';
680
681
const usePerformanceMonitor = (componentName: string, props: any) => {
682
const [renderCount, setRenderCount] = useState(0);
683
const [renderTimes, setRenderTimes] = useState<number[]>([]);
684
685
// Track renders in development
686
if (process.env.NODE_ENV === 'development') {
687
useWhyDidYouUpdate(componentName, props);
688
}
689
690
useEffect(() => {
691
const startTime = performance.now();
692
693
return () => {
694
const endTime = performance.now();
695
const renderTime = endTime - startTime;
696
697
setRenderCount(prev => prev + 1);
698
setRenderTimes(prev => [...prev.slice(-19), renderTime]); // Keep last 20 renders
699
};
700
});
701
702
const avgRenderTime = renderTimes.length > 0
703
? renderTimes.reduce((sum, time) => sum + time, 0) / renderTimes.length
704
: 0;
705
706
return {
707
renderCount,
708
avgRenderTime,
709
lastRenderTime: renderTimes[renderTimes.length - 1] || 0
710
};
711
};
712
713
// Performance dashboard component
714
const PerformanceDashboard = () => {
715
const [metrics, setMetrics] = useStore('performance.metrics', {});
716
717
return (
718
<div>
719
<h2>Performance Metrics</h2>
720
{Object.entries(metrics).map(([component, data]) => (
721
<div key={component}>
722
<h3>{component}</h3>
723
<p>Renders: {data.renderCount}</p>
724
<p>Avg Time: {data.avgRenderTime.toFixed(2)}ms</p>
725
</div>
726
))}
727
</div>
728
);
729
};
730
```
731
732
### Feature Flags System
733
734
```typescript
735
import { useStore } from 'react-admin';
736
737
const useFeatureFlags = () => {
738
const [flags, setFlags] = useStore('featureFlags', {});
739
740
const isEnabled = (feature: string): boolean => {
741
return flags[feature] === true;
742
};
743
744
const enableFeature = (feature: string) => {
745
setFlags({ ...flags, [feature]: true });
746
};
747
748
const disableFeature = (feature: string) => {
749
setFlags({ ...flags, [feature]: false });
750
};
751
752
const toggleFeature = (feature: string) => {
753
setFlags({ ...flags, [feature]: !flags[feature] });
754
};
755
756
return {
757
flags,
758
isEnabled,
759
enableFeature,
760
disableFeature,
761
toggleFeature
762
};
763
};
764
765
// Feature flag wrapper component
766
const FeatureGuard = ({ feature, children, fallback = null }) => {
767
const { isEnabled } = useFeatureFlags();
768
769
return isEnabled(feature) ? children : fallback;
770
};
771
772
// Usage
773
const AdminPanel = () => (
774
<div>
775
<FeatureGuard feature="advancedAnalytics">
776
<AdvancedAnalyticsWidget />
777
</FeatureGuard>
778
779
<FeatureGuard
780
feature="betaFeatures"
781
fallback={<div>Beta features coming soon!</div>}
782
>
783
<BetaFeaturePanel />
784
</FeatureGuard>
785
</div>
786
);
787
```
788
789
React Admin's advanced features provide powerful capabilities for building sophisticated, production-ready admin applications with comprehensive data management, user customization, performance monitoring, and extensibility options.