or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-core.mdadvanced.mdauth.mddata-management.mddetail-views.mdforms-inputs.mdi18n.mdindex.mdlayout-navigation.mdlists-data-display.mdui-components.md

advanced.mddocs/

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.