or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-setup.mdauthentication.mddata-operations.mdforms.mdindex.mdnavigation.mdtables-lists.mdutilities.md

utilities.mddocs/

0

# Utilities & Helpers

1

2

Resource management, loading states, metadata handling, notifications, and various utility functions for enhanced development experience.

3

4

## Capabilities

5

6

### Resource & Parameter Management

7

8

#### useResourceParams Hook

9

10

Gets resource and parameter information from current context with automatic inference from routes.

11

12

```typescript { .api }

13

/**

14

* Gets resource and parameter information from current context

15

* @param params - Resource parameter configuration

16

* @returns Current resource context and parameter values

17

*/

18

function useResourceParams(params?: UseResourceParamsConfig): UseResourceParamsReturnType;

19

20

interface UseResourceParamsConfig {

21

/** Override resource name */

22

resource?: string;

23

/** Override record ID */

24

id?: BaseKey;

25

/** Override action */

26

action?: string;

27

}

28

29

interface UseResourceParamsReturnType {

30

/** Current resource configuration */

31

resource?: IResourceItem;

32

/** Resource identifier (name) */

33

identifier?: string;

34

/** Current record ID */

35

id?: BaseKey;

36

/** Current action */

37

action?: Action;

38

/** Function to set ID */

39

setId: React.Dispatch<React.SetStateAction<BaseKey | undefined>>;

40

}

41

42

interface IResourceItem {

43

/** Resource name */

44

name: string;

45

/** Display label */

46

label?: string;

47

/** Resource icon */

48

icon?: React.ReactNode;

49

/** Whether resource can be deleted */

50

canDelete?: boolean;

51

/** Parent resource name */

52

parentName?: string;

53

/** Resource metadata */

54

meta?: Record<string, any>;

55

}

56

57

type Action = "list" | "create" | "edit" | "show" | "clone";

58

```

59

60

**Usage Example:**

61

62

```typescript

63

import { useResourceParams } from "@refinedev/core";

64

65

function ResourceHeader() {

66

const { resource, action, id } = useResourceParams();

67

68

const getTitle = () => {

69

if (!resource) return "Unknown Resource";

70

71

switch (action) {

72

case "list":

73

return `${resource.label || resource.name} List`;

74

case "create":

75

return `Create ${resource.label || resource.name}`;

76

case "edit":

77

return `Edit ${resource.label || resource.name} #${id}`;

78

case "show":

79

return `${resource.label || resource.name} #${id}`;

80

case "clone":

81

return `Clone ${resource.label || resource.name} #${id}`;

82

default:

83

return resource.label || resource.name;

84

}

85

};

86

87

return (

88

<header>

89

{resource?.icon}

90

<h1>{getTitle()}</h1>

91

</header>

92

);

93

}

94

95

// Dynamic component behavior based on resource

96

function ResourceActions() {

97

const { resource, action, id, setId } = useResourceParams();

98

99

const canEdit = resource?.meta?.permissions?.includes("edit");

100

const canDelete = resource?.canDelete !== false;

101

102

return (

103

<div className="resource-actions">

104

{action === "show" && canEdit && (

105

<button onClick={() => setId(id)}>

106

Edit {resource?.label}

107

</button>

108

)}

109

{action === "edit" && canDelete && (

110

<button className="danger">

111

Delete {resource?.label}

112

</button>

113

)}

114

</div>

115

);

116

}

117

```

118

119

### Loading & Performance

120

121

#### useLoadingOvertime Hook

122

123

Tracks loading time for performance monitoring and user experience optimization.

124

125

```typescript { .api }

126

/**

127

* Tracks loading time for performance monitoring

128

* @param params - Loading overtime configuration

129

* @returns Loading time information and callbacks

130

*/

131

function useLoadingOvertime(params: UseLoadingOvertimeConfig): UseLoadingOvertimeReturnType;

132

133

interface UseLoadingOvertimeConfig {

134

/** Whether currently loading */

135

isLoading: boolean;

136

/** Interval for time updates in milliseconds */

137

interval?: number;

138

/** Callback fired at each interval */

139

onInterval?: (elapsedTime: number) => void;

140

}

141

142

interface UseLoadingOvertimeReturnType {

143

/** Elapsed time since loading started in milliseconds */

144

elapsedTime?: number;

145

}

146

```

147

148

**Usage Example:**

149

150

```typescript

151

import { useLoadingOvertime, useList } from "@refinedev/core";

152

153

function DataTableWithLoadingIndicator() {

154

const { query } = useList({ resource: "posts" });

155

156

const { elapsedTime } = useLoadingOvertime({

157

isLoading: query.isLoading,

158

interval: 1000,

159

onInterval: (time) => {

160

if (time > 5000) {

161

console.warn("Query taking longer than expected:", time);

162

}

163

}

164

});

165

166

if (query.isLoading) {

167

return (

168

<div className="loading-state">

169

<div>Loading posts...</div>

170

{elapsedTime && elapsedTime > 2000 && (

171

<div className="loading-overtime">

172

Still loading... ({Math.round(elapsedTime / 1000)}s)

173

</div>

174

)}

175

</div>

176

);

177

}

178

179

return (

180

<div>

181

{/* Table content */}

182

</div>

183

);

184

}

185

```

186

187

### Metadata Management

188

189

#### useMeta Hook

190

191

Manages metadata for resources and operations with context-aware defaults.

192

193

```typescript { .api }

194

/**

195

* Manages metadata for resources and operations

196

* @param params - Meta configuration

197

* @returns Current metadata and update functions

198

*/

199

function useMeta(params?: UseMetaConfig): UseMetaReturnType;

200

201

interface UseMetaConfig {

202

/** Initial metadata */

203

meta?: MetaQuery;

204

/** Resource name for context */

205

resource?: string;

206

}

207

208

interface UseMetaReturnType {

209

/** Current metadata */

210

meta: MetaQuery;

211

/** Function to update metadata */

212

setMeta: (meta: MetaQuery) => void;

213

/** Function to merge metadata */

214

mergeMeta: (meta: MetaQuery) => void;

215

}

216

217

interface MetaQuery {

218

[key: string]: any;

219

}

220

```

221

222

#### useMetaContext Hook

223

224

Access to the meta context provider for global metadata management.

225

226

```typescript { .api }

227

/**

228

* Access to meta context provider

229

* @returns Meta context value and functions

230

*/

231

function useMetaContext(): MetaContextValue;

232

233

interface MetaContextValue {

234

/** Global metadata */

235

meta: MetaQuery;

236

/** Function to set global metadata */

237

setMeta: (meta: MetaQuery) => void;

238

}

239

```

240

241

### Notification System

242

243

#### useNotification Hook

244

245

Shows notifications with different types, positions, and auto-dismiss functionality.

246

247

```typescript { .api }

248

/**

249

* Shows notifications with customizable options

250

* @returns Notification functions for different types

251

*/

252

function useNotification(): UseNotificationReturnType;

253

254

interface UseNotificationReturnType {

255

/** Show success notification */

256

open: (params: OpenNotificationParams) => void;

257

/** Close specific notification */

258

close: (key: string) => void;

259

}

260

261

interface OpenNotificationParams {

262

/** Notification type */

263

type: "success" | "error" | "progress" | "warning" | "info";

264

/** Notification message */

265

message: string;

266

/** Additional description */

267

description?: string;

268

/** Unique key for the notification */

269

key?: string;

270

/** Whether notification should not auto-dismiss */

271

undoableTimeout?: number;

272

/** Whether to show undo button */

273

cancelMutation?: () => void;

274

/** Custom duration before auto-dismiss */

275

duration?: number;

276

}

277

```

278

279

**Usage Example:**

280

281

```typescript

282

import { useNotification, useCreate } from "@refinedev/core";

283

284

function CreatePostForm() {

285

const notification = useNotification();

286

const { mutate: createPost } = useCreate();

287

288

const handleCreate = (values: any) => {

289

createPost({

290

resource: "posts",

291

values

292

}, {

293

onSuccess: (data) => {

294

notification.open({

295

type: "success",

296

message: "Post Created",

297

description: `Post "${data.data.title}" has been created successfully.`,

298

duration: 4000

299

});

300

},

301

onError: (error) => {

302

notification.open({

303

type: "error",

304

message: "Creation Failed",

305

description: error.message || "Failed to create post."

306

});

307

}

308

});

309

};

310

311

return (

312

<form onSubmit={handleCreate}>

313

{/* Form fields */}

314

</form>

315

);

316

}

317

318

// Undoable notifications

319

function DeleteWithUndo({ postId }: { postId: string }) {

320

const notification = useNotification();

321

const { mutate: deletePost } = useDelete();

322

323

const handleDelete = () => {

324

let cancelMutation: (() => void) | undefined;

325

326

notification.open({

327

type: "success",

328

message: "Post Deleted",

329

description: "Post has been deleted.",

330

undoableTimeout: 5000,

331

cancelMutation: () => {

332

if (cancelMutation) {

333

cancelMutation();

334

notification.open({

335

type: "success",

336

message: "Deletion Cancelled",

337

description: "Post deletion has been cancelled."

338

});

339

}

340

}

341

});

342

343

// Set timeout for actual deletion

344

const timeoutId = setTimeout(() => {

345

deletePost({

346

resource: "posts",

347

id: postId

348

});

349

}, 5000);

350

351

cancelMutation = () => {

352

clearTimeout(timeoutId);

353

};

354

};

355

356

return <button onClick={handleDelete}>Delete Post</button>;

357

}

358

```

359

360

### Modal Management

361

362

#### useModal Hook

363

364

Manages modal state with multiple modal support and proper cleanup.

365

366

```typescript { .api }

367

/**

368

* Manages modal state with multiple modal support

369

* @returns Modal state and control functions

370

*/

371

function useModal(): UseModalReturnType;

372

373

interface UseModalReturnType {

374

/** Whether modal is visible */

375

visible: boolean;

376

/** Show the modal */

377

show: () => void;

378

/** Hide the modal */

379

close: () => void;

380

/** Toggle modal visibility */

381

toggle: () => void;

382

}

383

```

384

385

**Usage Example:**

386

387

```typescript

388

import { useModal, useOne } from "@refinedev/core";

389

390

function PostDetailsModal({ postId }: { postId?: string }) {

391

const modal = useModal();

392

const { data: post } = useOne({

393

resource: "posts",

394

id: postId!,

395

queryOptions: {

396

enabled: !!postId && modal.visible

397

}

398

});

399

400

return (

401

<>

402

<button onClick={modal.show}>View Details</button>

403

404

{modal.visible && (

405

<div className="modal-overlay" onClick={modal.close}>

406

<div className="modal-content" onClick={(e) => e.stopPropagation()}>

407

<div className="modal-header">

408

<h2>{post?.data.title}</h2>

409

<button onClick={modal.close}>×</button>

410

</div>

411

<div className="modal-body">

412

<p>{post?.data.content}</p>

413

</div>

414

</div>

415

</div>

416

)}

417

</>

418

);

419

}

420

421

// Multiple modals

422

function MultiModalExample() {

423

const editModal = useModal();

424

const deleteModal = useModal();

425

const viewModal = useModal();

426

427

return (

428

<div>

429

<button onClick={editModal.show}>Edit</button>

430

<button onClick={deleteModal.show}>Delete</button>

431

<button onClick={viewModal.show}>View</button>

432

433

{/* Multiple modal components */}

434

</div>

435

);

436

}

437

```

438

439

### Cache Management

440

441

#### useInvalidate Hook

442

443

Invalidates React Query cache with fine-grained control over cache keys.

444

445

```typescript { .api }

446

/**

447

* Invalidates React Query cache with fine-grained control

448

* @returns Cache invalidation functions

449

*/

450

function useInvalidate(): UseInvalidateReturnType;

451

452

interface UseInvalidateReturnType {

453

/** Invalidate specific cache entries */

454

invalidate: (params: InvalidateParams) => Promise<void>;

455

/** Invalidate all cache entries for a resource */

456

invalidateAll: (resource: string) => Promise<void>;

457

}

458

459

interface InvalidateParams {

460

/** Resource name */

461

resource: string;

462

/** Specific record ID */

463

id?: BaseKey;

464

/** Data provider name */

465

dataProviderName?: string;

466

/** Whether to invalidate exact match only */

467

exact?: boolean;

468

}

469

```

470

471

**Usage Example:**

472

473

```typescript

474

import { useInvalidate, useUpdate } from "@refinedev/core";

475

476

function RefreshablePostList() {

477

const invalidate = useInvalidate();

478

const { mutate: updatePost } = useUpdate();

479

480

const handleRefresh = async () => {

481

// Invalidate all posts data

482

await invalidate.invalidateAll("posts");

483

};

484

485

const handleUpdatePost = (id: string, values: any) => {

486

updatePost({

487

resource: "posts",

488

id,

489

values

490

}, {

491

onSuccess: async () => {

492

// Invalidate specific post and list

493

await invalidate.invalidate({ resource: "posts", id });

494

await invalidate.invalidate({ resource: "posts" });

495

}

496

});

497

};

498

499

return (

500

<div>

501

<button onClick={handleRefresh}>Refresh All</button>

502

{/* Post list */}

503

</div>

504

);

505

}

506

```

507

508

### Import/Export Utilities

509

510

#### File Processing Utilities

511

512

Utilities for handling file uploads, downloads, and format conversion.

513

514

```typescript { .api }

515

/**

516

* Convert file to base64 string

517

* @param file - File to convert

518

* @returns Promise resolving to base64 string

519

*/

520

function file2Base64(file: File): Promise<string>;

521

522

/**

523

* CSV import data mapper

524

* @param data - Raw CSV data

525

* @param mapping - Field mapping configuration

526

* @returns Mapped data array

527

*/

528

function importCSVMapper(

529

data: any[],

530

mapping: Record<string, string>

531

): any[];

532

533

interface FileProcessingOptions {

534

/** Maximum file size in bytes */

535

maxSize?: number;

536

/** Allowed file types */

537

allowedTypes?: string[];

538

/** Custom validation function */

539

validate?: (file: File) => boolean | string;

540

}

541

```

542

543

**Usage Example:**

544

545

```typescript

546

import { file2Base64, importCSVMapper } from "@refinedev/core";

547

548

function FileUploadComponent() {

549

const handleFileUpload = async (file: File) => {

550

try {

551

// Convert to base64 for API upload

552

const base64 = await file2Base64(file);

553

554

// Upload to server

555

await fetch("/api/upload", {

556

method: "POST",

557

body: JSON.stringify({ file: base64 }),

558

headers: { "Content-Type": "application/json" }

559

});

560

} catch (error) {

561

console.error("Upload failed:", error);

562

}

563

};

564

565

const handleCSVImport = (csvData: any[]) => {

566

// Map CSV columns to database fields

567

const mappedData = importCSVMapper(csvData, {

568

"Full Name": "name",

569

"Email Address": "email",

570

"Phone Number": "phone"

571

});

572

573

// Process mapped data

574

console.log("Imported data:", mappedData);

575

};

576

577

return (

578

<div>

579

<input

580

type="file"

581

accept=".csv,.xlsx"

582

onChange={(e) => {

583

const file = e.target.files?.[0];

584

if (file) handleFileUpload(file);

585

}}

586

/>

587

</div>

588

);

589

}

590

```

591

592

### Helper Functions

593

594

#### Query Key Management

595

596

Standardized query key generation for consistent React Query caching.

597

598

```typescript { .api }

599

/**

600

* Query key utilities for React Query

601

*/

602

interface QueryKeys {

603

/** Generate keys for list queries */

604

list: (resource: string, params?: any) => string[];

605

/** Generate keys for single record queries */

606

one: (resource: string, id: BaseKey, params?: any) => string[];

607

/** Generate keys for many records queries */

608

many: (resource: string, ids: BaseKey[], params?: any) => string[];

609

/** Generate keys for custom queries */

610

custom: (resource: string, params?: any) => string[];

611

}

612

613

/**

614

* Query key builder for advanced cache management

615

*/

616

class KeyBuilder {

617

/** Set resource name */

618

resource(name: string): KeyBuilder;

619

/** Set action type */

620

action(action: string): KeyBuilder;

621

/** Set parameters */

622

params(params: any): KeyBuilder;

623

/** Build the key array */

624

get(): string[];

625

}

626

```

627

628

#### URL & Route Utilities

629

630

Utilities for URL generation and route matching.

631

632

```typescript { .api }

633

/**

634

* Generate user-friendly names from resource identifiers

635

* @param name - Resource name or identifier

636

* @returns User-friendly display name

637

*/

638

function useUserFriendlyName(): (name: string) => string;

639

640

/**

641

* Match resource from current route

642

* @param route - Current route path

643

* @param resources - Available resources

644

* @returns Matched resource configuration

645

*/

646

function matchResourceFromRoute(

647

route: string,

648

resources: IResourceItem[]

649

): IResourceItem | undefined;

650

651

/**

652

* Generate default document title

653

* @param params - Title generation parameters

654

* @returns Generated document title

655

*/

656

function generateDefaultDocumentTitle(params: {

657

resource?: string;

658

action?: string;

659

id?: BaseKey;

660

}): string;

661

```

662

663

#### Object Utilities

664

665

Utilities for object manipulation and property access.

666

667

```typescript { .api }

668

/**

669

* Flatten nested object keys with dot notation

670

* @param obj - Object to flatten

671

* @param prefix - Key prefix for nested properties

672

* @returns Flattened object

673

*/

674

function flattenObjectKeys(

675

obj: Record<string, any>,

676

prefix?: string

677

): Record<string, any>;

678

679

/**

680

* Convert property path string to array

681

* @param path - Property path (e.g., "user.profile.name")

682

* @returns Path array (e.g., ["user", "profile", "name"])

683

*/

684

function propertyPathToArray(path: string): string[];

685

```

686

687

**Usage Example:**

688

689

```typescript

690

import {

691

flattenObjectKeys,

692

propertyPathToArray,

693

useUserFriendlyName

694

} from "@refinedev/core";

695

696

function ObjectUtilsExample() {

697

const getUserFriendlyName = useUserFriendlyName();

698

699

// Flatten nested object

700

const nestedData = {

701

user: {

702

profile: {

703

name: "John Doe",

704

address: {

705

city: "New York",

706

country: "USA"

707

}

708

}

709

}

710

};

711

712

const flatData = flattenObjectKeys(nestedData);

713

// Result: { "user.profile.name": "John Doe", "user.profile.address.city": "New York", ... }

714

715

// Convert path to array

716

const pathArray = propertyPathToArray("user.profile.name");

717

// Result: ["user", "profile", "name"]

718

719

// Generate friendly names

720

const friendlyName = getUserFriendlyName("user_profiles");

721

// Result: "User Profiles"

722

723

return (

724

<div>

725

<h2>{friendlyName}</h2>

726

<pre>{JSON.stringify(flatData, null, 2)}</pre>

727

</div>

728

);

729

}

730

```

731

732

## Types

733

734

```typescript { .api }

735

interface UtilityConfig {

736

/** Default configuration values */

737

defaults?: Record<string, any>;

738

/** Feature flags */

739

features?: Record<string, boolean>;

740

/** Custom settings */

741

settings?: Record<string, any>;

742

}

743

744

interface ProcessingResult<T> {

745

/** Processing success status */

746

success: boolean;

747

/** Processed data */

748

data?: T;

749

/** Processing errors */

750

errors?: string[];

751

/** Warning messages */

752

warnings?: string[];

753

}

754

755

interface CacheOptions {

756

/** Cache expiry time in milliseconds */

757

expiry?: number;

758

/** Whether to use stale data while revalidating */

759

staleWhileRevalidate?: boolean;

760

/** Custom cache key */

761

key?: string;

762

}

763

764

interface ErrorBoundaryProps {

765

/** Fallback component for errors */

766

fallback?: React.ComponentType<{ error: Error }>;

767

/** Error callback */

768

onError?: (error: Error, errorInfo: any) => void;

769

/** Children to wrap */

770

children: React.ReactNode;

771

}

772

```

773

774

### Real-time & Live Data

775

776

#### usePublish Hook

777

778

Publishes real-time events to subscribed clients for live data synchronization.

779

780

```typescript { .api }

781

/**

782

* Publishes real-time events to subscribed clients

783

* @returns Publish function from the live provider

784

*/

785

function usePublish(): ((event: LiveEvent) => void) | undefined;

786

787

interface LiveEvent {

788

/** Channel name for the event */

789

channel: string;

790

/** Event type */

791

type: "created" | "updated" | "deleted" | string;

792

/** Event payload */

793

payload: {

794

/** IDs of affected records */

795

ids?: BaseKey[];

796

/** Additional event data */

797

[key: string]: any;

798

};

799

/** Timestamp of the event */

800

date: Date;

801

}

802

```

803

804

**Usage Example:**

805

806

```typescript

807

import { usePublish } from "@refinedev/core";

808

809

function ProductForm() {

810

const publish = usePublish();

811

812

const handleProductUpdate = (product) => {

813

// Update product logic...

814

815

// Notify other clients about the update

816

publish?.({

817

channel: "products",

818

type: "updated",

819

payload: {

820

ids: [product.id]

821

},

822

date: new Date()

823

});

824

};

825

826

return (

827

<form onSubmit={handleProductUpdate}>

828

{/* Form fields */}

829

</form>

830

);

831

}

832

```

833

834

#### useSubscription Hook

835

836

Subscribes to real-time events for live data synchronization and automatic updates.

837

838

```typescript { .api }

839

/**

840

* Subscribes to real-time events for live data updates

841

* @param params - Subscription configuration

842

*/

843

function useSubscription(params: UseSubscriptionConfig): void;

844

845

interface UseSubscriptionConfig {

846

/** Channel name to subscribe to */

847

channel: string;

848

/** Callback function when live events are received */

849

onLiveEvent: (event: LiveEvent) => void;

850

/** Types of events to subscribe to */

851

types?: Array<"created" | "updated" | "deleted" | "*" | string>;

852

/** Whether subscription is enabled */

853

enabled?: boolean;

854

/** Additional parameters for the subscription */

855

params?: {

856

ids?: BaseKey[];

857

id?: BaseKey;

858

sorters?: CrudSort[];

859

filters?: CrudFilter[];

860

subscriptionType?: "useList" | "useOne" | "useMany";

861

resource?: string;

862

[key: string]: any;

863

};

864

/** Metadata for the subscription */

865

meta?: MetaQuery & { dataProviderName?: string };

866

}

867

```

868

869

**Usage Example:**

870

871

```typescript

872

import { useSubscription, useList } from "@refinedev/core";

873

874

function ProductsList() {

875

const { data, refetch } = useList({ resource: "products" });

876

877

// Subscribe to product updates

878

useSubscription({

879

channel: "products",

880

onLiveEvent: (event) => {

881

if (event.type === "created" || event.type === "updated" || event.type === "deleted") {

882

// Refetch the list when products are modified

883

refetch();

884

}

885

},

886

types: ["created", "updated", "deleted"],

887

enabled: true

888

});

889

890

return (

891

<div>

892

{data?.data.map(product => (

893

<div key={product.id}>{product.name}</div>

894

))}

895

</div>

896

);

897

}

898

```

899

900

#### useLiveMode Hook

901

902

Manages live mode state and automatic real-time updates for data operations.

903

904

```typescript { .api }

905

/**

906

* Manages live mode state for automatic real-time updates

907

* @returns Live mode configuration and state

908

*/

909

function useLiveMode(): UseLiveModeReturnType;

910

911

interface UseLiveModeReturnType {

912

/** Current live mode setting */

913

liveMode?: "auto" | "manual" | "off";

914

}

915

```

916

917

**Usage Example:**

918

919

```typescript

920

import { useLiveMode, useList } from "@refinedev/core";

921

922

function ProductsList() {

923

const { liveMode } = useLiveMode();

924

925

const { data } = useList({

926

resource: "products",

927

liveMode: liveMode, // Use global live mode setting

928

});

929

930

return (

931

<div>

932

<p>Live mode: {liveMode}</p>

933

{data?.data.map(product => (

934

<div key={product.id}>{product.name}</div>

935

))}

936

</div>

937

);

938

}

939

```

940

941

### Menu & Navigation

942

943

#### useMenu Hook

944

945

Generates menu items for navigation based on available resources, with hierarchical structure support and customizable visibility.

946

947

```typescript { .api }

948

/**

949

* Generates menu items for navigation sidebars and menus

950

* @param params - Menu configuration options

951

* @returns Menu items with navigation structure

952

*/

953

function useMenu(params?: UseMenuConfig): UseMenuReturnType;

954

955

interface UseMenuConfig {

956

/** Additional metadata for menu generation */

957

meta?: Record<string, any>;

958

/** Hide menu items that have missing route parameters */

959

hideOnMissingParameter?: boolean;

960

}

961

962

interface UseMenuReturnType {

963

/** Keys of menu items that should be open by default */

964

defaultOpenKeys: string[];

965

/** Currently selected menu item key */

966

selectedKey: string;

967

/** Hierarchical menu items structure */

968

menuItems: TreeMenuItem[];

969

}

970

971

interface TreeMenuItem {

972

/** Menu item key/identifier */

973

key: string;

974

/** Display label for the menu item */

975

label?: string;

976

/** Icon component for the menu item */

977

icon?: React.ReactNode;

978

/** Route path for navigation */

979

route?: string;

980

/** Child menu items */

981

children: TreeMenuItem[];

982

/** Resource metadata */

983

resource?: IResourceItem;

984

/** Parent resource reference */

985

parentName?: string;

986

}

987

```

988

989

**Usage Example:**

990

991

```typescript

992

import { useMenu } from "@refinedev/core";

993

994

function Sidebar() {

995

const { menuItems, selectedKey, defaultOpenKeys } = useMenu({

996

hideOnMissingParameter: true

997

});

998

999

const renderMenuItem = (item: TreeMenuItem) => (

1000

<div key={item.key} className={selectedKey === item.key ? "active" : ""}>

1001

{item.icon}

1002

<span>{item.label}</span>

1003

{item.children.length > 0 && (

1004

<div>

1005

{item.children.map(renderMenuItem)}

1006

</div>

1007

)}

1008

</div>

1009

);

1010

1011

return (

1012

<nav>

1013

{menuItems.map(renderMenuItem)}

1014

</nav>

1015

);

1016

}

1017

```