or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-system.mddata-display.mddate-time.mdfeedback.mdforms.mdindex.mdinputs.mdinteractions.mdlayout.mdnavigation.mdoverlays.mdutilities.md

utilities.mddocs/

0

# Utilities and Hooks

1

2

NextUI provides a comprehensive set of utility functions, custom hooks, and helper types that extend functionality and enable advanced customization patterns for building sophisticated user interfaces.

3

4

## Capabilities

5

6

### Disclosure Hook

7

8

A versatile hook for managing open/closed state in overlay components, modals, popovers, and other interactive elements.

9

10

```typescript { .api }

11

interface UseDisclosureProps {

12

/** Whether overlay is open */

13

isOpen?: boolean;

14

/** Default open state for uncontrolled mode */

15

defaultOpen?: boolean;

16

/** Close event handler */

17

onClose?: () => void;

18

/** Open change event handler */

19

onOpenChange?: (isOpen: boolean) => void;

20

/** Unique identifier for the disclosure */

21

id?: string;

22

}

23

24

interface UseDisclosureReturn {

25

/** Current open state */

26

isOpen: boolean;

27

/** Function to open the overlay */

28

onOpen: () => void;

29

/** Function to close the overlay */

30

onClose: () => void;

31

/** Function to toggle overlay state */

32

onOpenChange: (isOpen: boolean) => void;

33

/** Function to toggle between open and closed */

34

onToggle: () => void;

35

/** Whether the state is controlled externally */

36

isControlled: boolean;

37

/** Props for trigger button */

38

getButtonProps: (props?: any) => any;

39

/** Props for disclosure content */

40

getDisclosureProps: (props?: any) => any;

41

}

42

43

/**

44

* Hook for managing disclosure state (open/closed) for overlays

45

* @param props Configuration options

46

* @returns Disclosure state and handlers

47

*/

48

function useDisclosure(props?: UseDisclosureProps): UseDisclosureReturn;

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import { useDisclosure, Modal, ModalContent, Button } from "@nextui-org/react";

55

56

function DisclosureExample() {

57

// Basic usage

58

const { isOpen, onOpen, onOpenChange } = useDisclosure();

59

60

// Controlled usage

61

const [modalOpen, setModalOpen] = useState(false);

62

const modal = useDisclosure({

63

isOpen: modalOpen,

64

onOpenChange: setModalOpen,

65

});

66

67

// With default state

68

const drawer = useDisclosure({ defaultOpen: true });

69

70

return (

71

<div className="space-y-4">

72

{/* Basic modal */}

73

<Button onPress={onOpen}>Open Modal</Button>

74

<Modal isOpen={isOpen} onOpenChange={onOpenChange}>

75

<ModalContent>

76

<p>Modal content</p>

77

</ModalContent>

78

</Modal>

79

80

{/* Controlled modal */}

81

<Button onPress={modal.onOpen}>Controlled Modal</Button>

82

<Modal isOpen={modal.isOpen} onOpenChange={modal.onOpenChange}>

83

<ModalContent>

84

<p>Controlled modal content</p>

85

</ModalContent>

86

</Modal>

87

88

{/* Multiple disclosures */}

89

<Button onPress={drawer.onToggle}>

90

{drawer.isOpen ? "Close" : "Open"} Drawer

91

</Button>

92

</div>

93

);

94

}

95

```

96

97

### Draggable Hook

98

99

Advanced hook for implementing drag functionality with constraints and gestures.

100

101

```typescript { .api }

102

interface UseDraggableProps {

103

/** Whether dragging is disabled */

104

isDisabled?: boolean;

105

/** Target element reference */

106

targetRef?: React.RefObject<HTMLElement>;

107

/** Body element reference for constraints */

108

bodyRef?: React.RefObject<HTMLElement>;

109

/** Drag constraint boundaries */

110

dragConstraints?: {

111

top?: number;

112

left?: number;

113

right?: number;

114

bottom?: number;

115

} | React.RefObject<Element>;

116

/** Elastic drag behavior */

117

dragElastic?: boolean | number;

118

/** Drag momentum configuration */

119

dragMomentum?: boolean;

120

/** Drag transition spring configuration */

121

dragTransition?: {

122

bounceStiffness?: number;

123

bounceDamping?: number;

124

};

125

}

126

127

interface UseDraggableReturn {

128

/** Motion props for the draggable element */

129

dragProps: {

130

drag?: boolean | "x" | "y";

131

dragConstraints?: any;

132

dragElastic?: boolean | number;

133

dragMomentum?: boolean;

134

dragTransition?: any;

135

dragControls?: any;

136

onDragStart?: (event: any, info: any) => void;

137

onDrag?: (event: any, info: any) => void;

138

onDragEnd?: (event: any, info: any) => void;

139

};

140

/** Drag controls for programmatic control */

141

dragControls: any;

142

}

143

144

/**

145

* Hook for implementing draggable functionality with Framer Motion

146

* @param props Drag configuration options

147

* @returns Drag props and controls

148

*/

149

function useDraggable(props: UseDraggableProps): UseDraggableReturn;

150

```

151

152

**Usage Example:**

153

154

```typescript

155

import { useDraggable, Card, CardBody } from "@nextui-org/react";

156

import { motion } from "framer-motion";

157

158

function DraggableExample() {

159

const constraintsRef = useRef<HTMLDivElement>(null);

160

const { dragProps } = useDraggable({

161

dragConstraints: constraintsRef,

162

dragElastic: 0.1,

163

});

164

165

return (

166

<div

167

ref={constraintsRef}

168

className="w-96 h-96 bg-default-100 rounded-lg relative"

169

>

170

<motion.div {...dragProps} className="absolute">

171

<Card className="w-32 h-32 cursor-grab active:cursor-grabbing">

172

<CardBody className="flex items-center justify-center">

173

<p className="text-sm">Drag me!</p>

174

</CardBody>

175

</Card>

176

</motion.div>

177

</div>

178

);

179

}

180

```

181

182

### Ripple Hook

183

184

Hook for implementing Material Design ripple effects on interactive elements.

185

186

```typescript { .api }

187

interface UseRippleProps {

188

/** Ripple color theme */

189

color?: "current" | "white" | "default" | "primary" | "secondary" | "success" | "warning" | "danger";

190

/** Whether ripple is disabled */

191

isDisabled?: boolean;

192

/** Disable ripple for specific interactions */

193

disableRipple?: boolean;

194

/** Custom ripple duration in milliseconds */

195

duration?: number;

196

}

197

198

interface UseRippleReturn {

199

/** Ripple component to render */

200

ripples: React.ReactNode;

201

/** Function to trigger ripple effect */

202

onClick: (event: React.MouseEvent) => void;

203

/** Clear all active ripples */

204

clear: () => void;

205

}

206

207

/**

208

* Hook for Material Design ripple effects

209

* @param props Ripple configuration

210

* @returns Ripple state and handlers

211

*/

212

function useRipple(props?: UseRippleProps): UseRippleReturn;

213

```

214

215

**Usage Example:**

216

217

```typescript

218

import { useRipple, Card, CardBody } from "@nextui-org/react";

219

220

function RippleExample() {

221

const { ripples, onClick } = useRipple({

222

color: "primary",

223

});

224

225

return (

226

<Card

227

isPressable

228

className="w-64 h-32 relative overflow-hidden"

229

onPress={onClick}

230

>

231

<CardBody className="flex items-center justify-center">

232

<p>Click for ripple effect</p>

233

{ripples}

234

</CardBody>

235

</Card>

236

);

237

}

238

```

239

240

### Scroll Shadow Hook

241

242

Hook for implementing scroll shadows that indicate scrollable content overflow.

243

244

```typescript { .api }

245

interface UseScrollShadowProps {

246

/** Scroll container reference */

247

ref?: React.RefObject<HTMLElement>;

248

/** Shadow visibility configuration */

249

visibility?: "auto" | "top" | "bottom" | "left" | "right" | "both" | "none";

250

/** Scroll orientation for shadow calculation */

251

orientation?: "vertical" | "horizontal";

252

/** Whether shadows are enabled */

253

isEnabled?: boolean;

254

/** Shadow size in pixels */

255

size?: number;

256

/** Offset threshold before showing shadows */

257

offset?: number;

258

/** Update interval for scroll position checking */

259

updateInterval?: number;

260

}

261

262

interface UseScrollShadowReturn {

263

/** Scroll container ref to attach */

264

scrollRef: React.RefObject<HTMLElement>;

265

/** Whether top shadow should be visible */

266

isTopShadowVisible: boolean;

267

/** Whether bottom shadow should be visible */

268

isBottomShadowVisible: boolean;

269

/** Whether left shadow should be visible */

270

isLeftShadowVisible: boolean;

271

/** Whether right shadow should be visible */

272

isRightShadowVisible: boolean;

273

/** Current scroll position */

274

scrollPosition: { top: number; left: number };

275

/** Scroll container dimensions */

276

dimensions: {

277

scrollWidth: number;

278

scrollHeight: number;

279

clientWidth: number;

280

clientHeight: number;

281

};

282

}

283

284

/**

285

* Hook for scroll shadow functionality

286

* @param props Scroll shadow configuration

287

* @returns Scroll shadow state and refs

288

*/

289

function useScrollShadow(props?: UseScrollShadowProps): UseScrollShadowReturn;

290

```

291

292

**Usage Example:**

293

294

```typescript

295

import { useScrollShadow, Card, CardBody } from "@nextui-org/react";

296

297

function ScrollShadowExample() {

298

const {

299

scrollRef,

300

isTopShadowVisible,

301

isBottomShadowVisible

302

} = useScrollShadow({

303

orientation: "vertical",

304

size: 8,

305

});

306

307

const items = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`);

308

309

return (

310

<Card className="w-64 h-96 relative">

311

<CardBody className="p-0">

312

{/* Top shadow */}

313

{isTopShadowVisible && (

314

<div className="absolute top-0 left-0 right-0 h-8 bg-gradient-to-b from-background to-transparent z-10 pointer-events-none" />

315

)}

316

317

{/* Scrollable content */}

318

<div

319

ref={scrollRef}

320

className="h-full overflow-y-auto p-4 space-y-2"

321

>

322

{items.map((item) => (

323

<div key={item} className="p-2 bg-default-100 rounded">

324

{item}

325

</div>

326

))}

327

</div>

328

329

{/* Bottom shadow */}

330

{isBottomShadowVisible && (

331

<div className="absolute bottom-0 left-0 right-0 h-8 bg-gradient-to-t from-background to-transparent z-10 pointer-events-none" />

332

)}

333

</CardBody>

334

</Card>

335

);

336

}

337

```

338

339

### Provider Context Hook

340

341

Hook for accessing NextUI provider configuration throughout the component tree.

342

343

```typescript { .api }

344

interface ProviderContextProps {

345

/** Current theme */

346

theme?: "light" | "dark";

347

/** Available themes */

348

themes?: ConfigThemes;

349

/** Default theme key */

350

defaultTheme?: string;

351

/** Whether animations are disabled globally */

352

disableAnimation?: boolean;

353

/** Whether ripple effects are disabled globally */

354

disableRipple?: boolean;

355

/** Validation behavior mode */

356

validationBehavior?: "aria" | "native";

357

/** Current locale */

358

locale?: string;

359

/** Navigation function for client-side routing */

360

navigate?: (path: string) => void;

361

/** Whether portal container is created */

362

createPortalContainer?: boolean;

363

}

364

365

/**

366

* Hook to access NextUI provider context

367

* @returns Provider context props or undefined if outside provider

368

*/

369

function useProviderContext(): ProviderContextProps | undefined;

370

```

371

372

**Usage Example:**

373

374

```typescript

375

import { useProviderContext, Button, Card, CardBody } from "@nextui-org/react";

376

377

function ThemeAwareComponent() {

378

const context = useProviderContext();

379

380

const handleThemeToggle = () => {

381

// Access theme configuration

382

const currentTheme = context?.theme;

383

console.log("Current theme:", currentTheme);

384

};

385

386

return (

387

<Card>

388

<CardBody>

389

<p>Current theme: {context?.theme || "No theme"}</p>

390

<p>Animations: {context?.disableAnimation ? "Disabled" : "Enabled"}</p>

391

<p>Locale: {context?.locale || "Default"}</p>

392

<Button

393

onPress={handleThemeToggle}

394

color="primary"

395

>

396

Check Theme

397

</Button>

398

</CardBody>

399

</Card>

400

);

401

}

402

```

403

404

### Forwarded Ref Utility

405

406

Enhanced forwardRef function that works seamlessly with NextUI's component system.

407

408

```typescript { .api }

409

/**

410

* Enhanced forwardRef function for NextUI components

411

* @param render Render function that receives props and ref

412

* @returns Forwarded ref component

413

*/

414

function forwardRef<T, P = {}>(

415

render: (props: P, ref: React.Ref<T>) => React.ReactElement | null

416

): (props: P & React.RefAttributes<T>) => React.ReactElement | null;

417

```

418

419

**Usage Example:**

420

421

```typescript

422

import { forwardRef, Button } from "@nextui-org/react";

423

424

interface CustomButtonProps {

425

variant?: "primary" | "secondary";

426

children: React.ReactNode;

427

}

428

429

const CustomButton = forwardRef<HTMLButtonElement, CustomButtonProps>(

430

({ variant = "primary", children, ...props }, ref) => {

431

return (

432

<Button

433

ref={ref}

434

color={variant}

435

{...props}

436

>

437

{children}

438

</Button>

439

);

440

}

441

);

442

443

// Usage

444

function App() {

445

const buttonRef = useRef<HTMLButtonElement>(null);

446

447

return (

448

<CustomButton

449

ref={buttonRef}

450

variant="secondary"

451

onPress={() => buttonRef.current?.focus()}

452

>

453

Custom Button

454

</CustomButton>

455

);

456

}

457

```

458

459

### Class Name Utilities

460

461

Utility functions for working with CSS classes and styling.

462

463

```typescript { .api }

464

/**

465

* Class name utility (alias for clsx/cn)

466

* @param inputs Class name inputs (strings, objects, arrays)

467

* @returns Merged class name string

468

*/

469

function cn(...inputs: ClassValue[]): string;

470

471

type ClassValue =

472

| string

473

| number

474

| boolean

475

| undefined

476

| null

477

| ClassValue[]

478

| Record<string, any>;

479

480

/**

481

* Merge classes utility for combining class strings

482

* @param classes Class name strings to merge

483

* @returns Combined class string

484

*/

485

function mergeClasses(...classes: string[]): string;

486

```

487

488

**Usage Examples:**

489

490

```typescript

491

import { cn, Card, CardBody } from "@nextui-org/react";

492

493

function ClassUtilityExample() {

494

const [isActive, setIsActive] = useState(false);

495

const [variant, setVariant] = useState<"primary" | "secondary">("primary");

496

497

// Using cn for conditional classes

498

const cardClasses = cn(

499

"transition-all duration-200",

500

{

501

"scale-105 shadow-lg": isActive,

502

"bg-primary": variant === "primary",

503

"bg-secondary": variant === "secondary",

504

}

505

);

506

507

// Using cn with arrays

508

const buttonClasses = cn([

509

"px-4 py-2 rounded",

510

isActive && "bg-primary text-white",

511

!isActive && "bg-gray-200 text-gray-700"

512

]);

513

514

return (

515

<div className="space-y-4">

516

<Card

517

className={cardClasses}

518

isPressable

519

onPress={() => setIsActive(!isActive)}

520

>

521

<CardBody>

522

<p>Click to toggle active state</p>

523

<p>Current variant: {variant}</p>

524

</CardBody>

525

</Card>

526

527

<button

528

className={buttonClasses}

529

onClick={() => setIsActive(!isActive)}

530

>

531

Toggle Active

532

</button>

533

</div>

534

);

535

}

536

```

537

538

### Iterator Utilities

539

540

Utilities for working with iterables and collections.

541

542

```typescript { .api }

543

/**

544

* Convert value to iterator

545

* @param value Iterable or function that returns iterable

546

* @returns Iterator instance

547

*/

548

function toIterator<T>(value: Iterable<T> | (() => Iterable<T>)): Iterator<T>;

549

550

/**

551

* Check if element is a NextUI component

552

* @param element React element to check

553

* @returns Whether element is NextUI component

554

*/

555

function isNextUIEl(element: React.ReactElement): boolean;

556

```

557

558

**Usage Example:**

559

560

```typescript

561

import { toIterator, isNextUIEl, Button, Card } from "@nextui-org/react";

562

563

function IteratorExample() {

564

// Convert array to iterator

565

const items = ["apple", "banana", "cherry"];

566

const iterator = toIterator(items);

567

568

// Convert generator function to iterator

569

function* numberGenerator() {

570

for (let i = 0; i < 5; i++) {

571

yield i;

572

}

573

}

574

const numberIterator = toIterator(numberGenerator);

575

576

// Check if elements are NextUI components

577

const checkComponents = () => {

578

const button = <Button>Test</Button>;

579

const div = <div>Test</div>;

580

const card = <Card>Test</Card>;

581

582

console.log("Button is NextUI:", isNextUIEl(button)); // true

583

console.log("Div is NextUI:", isNextUIEl(div)); // false

584

console.log("Card is NextUI:", isNextUIEl(card)); // true

585

};

586

587

return (

588

<div className="space-y-4">

589

<Button onPress={checkComponents}>

590

Check Components

591

</Button>

592

593

<div>

594

<p>Iterator example:</p>

595

<ul>

596

{Array.from(iterator).map((item, index) => (

597

<li key={index}>{item}</li>

598

))}

599

</ul>

600

</div>

601

</div>

602

);

603

}

604

```

605

606

### Prop Mapping Utilities

607

608

Utilities for component variant and prop management.

609

610

```typescript { .api }

611

/**

612

* Map props to variants and common props

613

* @param props Component props

614

* @param variantKeys Keys that should be treated as variants

615

* @param removeVariantProps Whether to remove variant props from main props

616

* @returns Tuple of [remainingProps, variantProps]

617

*/

618

function mapPropsVariants<T, K extends keyof T>(

619

props: T,

620

variantKeys: K[],

621

removeVariantProps?: boolean

622

): [Omit<T, K>, Pick<T, K>];

623

624

/**

625

* Map props to variants and common props with additional common keys

626

* @param props Component props

627

* @param variantKeys Keys that should be treated as variants

628

* @param commonKeys Keys that should be treated as common props

629

* @param removeVariantProps Whether to remove variant props from main props

630

* @returns Tuple of [remainingProps, variantProps, commonProps]

631

*/

632

function mapPropsVariantsWithCommon<T, K extends keyof T, C extends keyof T>(

633

props: T,

634

variantKeys: K[],

635

commonKeys: C[],

636

removeVariantProps?: boolean

637

): [Omit<T, K | C>, Pick<T, K>, Pick<T, C>];

638

```

639

640

### Component Extension Utilities

641

642

Utilities for extending and customizing NextUI components.

643

644

```typescript { .api }

645

/**

646

* Extend component variants with custom styling

647

* @param component Component function created with tv()

648

* @param variants Additional variant configurations

649

* @param defaultVariants Default variant selections

650

* @returns Extended component function

651

*/

652

function extendVariants<T extends (...args: any[]) => any>(

653

component: T,

654

variants: ExtendVariantProps<T>,

655

defaultVariants?: Record<string, any>

656

): T;

657

658

interface ExtendVariantProps<T> {

659

/** Additional variants to add */

660

variants?: Record<string, Record<string, any>>;

661

/** Default variant values */

662

defaultVariants?: Record<string, any>;

663

/** Compound variants for complex styling */

664

compoundVariants?: Array<{

665

/** Variant conditions */

666

[key: string]: any;

667

/** Classes to apply when conditions match */

668

class?: string | string[];

669

}>;

670

}

671

```

672

673

**Usage Example:**

674

675

```typescript

676

import { extendVariants, Button } from "@nextui-org/react";

677

678

// Extend Button with custom variants

679

const CustomButton = extendVariants(Button, {

680

variants: {

681

color: {

682

olive: "bg-[#84cc16] text-[#000] data-[hover=true]:bg-[#65a30d]",

683

orange: "bg-[#ff8c00] text-[#fff] data-[hover=true]:bg-[#ff7700]",

684

violet: "bg-[#8b5cf6] text-[#fff] data-[hover=true]:bg-[#7c3aed]",

685

},

686

isDisabled: {

687

true: "bg-[#eaeaea] text-[#000] opacity-50 cursor-not-allowed",

688

},

689

size: {

690

xs: "px-2 py-1 text-xs",

691

xl: "px-8 py-4 text-xl",

692

},

693

},

694

defaultVariants: {

695

color: "olive",

696

size: "md",

697

},

698

compoundVariants: [

699

{

700

color: "olive",

701

size: "xl",

702

class: "uppercase font-bold",

703

},

704

],

705

});

706

707

function ExtendVariantsExample() {

708

return (

709

<div className="flex gap-4 items-center">

710

<CustomButton color="olive" size="sm">

711

Olive Button

712

</CustomButton>

713

<CustomButton color="orange" size="md">

714

Orange Button

715

</CustomButton>

716

<CustomButton color="violet" size="xl">

717

Violet XL Button

718

</CustomButton>

719

</div>

720

);

721

}

722

```

723

724

## Utility Types

725

726

```typescript { .api }

727

// Common utility types

728

type Size = "sm" | "md" | "lg";

729

type Color = "default" | "primary" | "secondary" | "success" | "warning" | "danger";

730

type Radius = "none" | "sm" | "md" | "lg" | "full";

731

type Variant = "solid" | "bordered" | "light" | "flat" | "faded" | "shadow";

732

733

// Component polymorphism types

734

type As<Props = any> = React.ElementType<Props>;

735

736

type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {

737

as?: T;

738

};

739

740

type OmitCommonProps<T, K extends keyof any = never> = Omit<T, "id" | "children" | K>;

741

742

type RightJoinProps<

743

SourceProps extends object = {},

744

OverrideProps extends object = {}

745

> = OmitCommonProps<SourceProps, keyof OverrideProps> & OverrideProps;

746

747

type MergeWithAs<

748

ComponentProps extends object,

749

AsProps extends object,

750

AdditionalProps extends object = {},

751

AsComponent extends As = As

752

> = RightJoinProps<ComponentProps, AdditionalProps> &

753

RightJoinProps<AsProps, AdditionalProps> & {

754

as?: AsComponent;

755

};

756

757

// Prop getter types

758

type PropGetter<P = Record<string, unknown>, R = Record<string, unknown>> = (

759

props?: P,

760

forwardedRef?: React.Ref<any>

761

) => R;

762

763

// Slot-based styling types

764

type SlotsToClasses<S extends string> = {

765

[K in S]?: string;

766

};

767

768

type SlotProps<T> = T extends Record<infer S, any> ? S : never;

769

770

// Theme and variant types

771

interface ConfigThemes {

772

light?: ConfigTheme;

773

dark?: ConfigTheme;

774

[key: string]: ConfigTheme | undefined;

775

}

776

777

interface ConfigTheme {

778

colors?: Record<string, any>;

779

layout?: LayoutTheme;

780

}

781

782

interface LayoutTheme {

783

spacingUnit?: number;

784

disableAnimation?: boolean;

785

radius?: BaseThemeUnit;

786

borderWidth?: BaseThemeUnit;

787

}

788

789

interface BaseThemeUnit {

790

small?: string;

791

medium?: string;

792

large?: string;

793

}

794

795

// Selection types

796

type Selection = "all" | Set<React.Key>;

797

type SelectionMode = "none" | "single" | "multiple";

798

type SelectionBehavior = "toggle" | "replace";

799

800

interface SharedSelection {

801

selectedKeys?: Selection;

802

defaultSelectedKeys?: Selection;

803

selectionMode?: SelectionMode;

804

selectionBehavior?: SelectionBehavior;

805

disallowEmptySelection?: boolean;

806

onSelectionChange?: (keys: Selection) => void;

807

}

808

809

// Validation types

810

type ValidationBehavior = "aria" | "native";

811

type ValidationError = string | string[];

812

813

interface ValidationResult {

814

isInvalid: boolean;

815

validationErrors: string[];

816

validationDetails: ValidationDetails;

817

}

818

819

interface ValidationDetails {

820

[key: string]: any;

821

}

822

823

// Motion and animation types

824

interface MotionProps {

825

initial?: any;

826

animate?: any;

827

exit?: any;

828

transition?: any;

829

variants?: any;

830

whileHover?: any;

831

whileTap?: any;

832

whileFocus?: any;

833

whileInView?: any;

834

}

835

836

// Placement types for overlays

837

type PlacementAxis = "top" | "bottom" | "left" | "right";

838

type PlacementAlign = "start" | "end";

839

type Placement = PlacementAxis | `${PlacementAxis}-${PlacementAlign}`;

840

841

// Date/Time utility types

842

type DateValue = CalendarDate | CalendarDateTime | ZonedDateTime;

843

type TimeValue = Time;

844

845

interface RangeValue<T> {

846

start: T;

847

end: T;

848

}

849

850

// Hook return type patterns

851

interface UseComponentReturn<TElement = HTMLElement> {

852

Component: React.ElementType;

853

domRef: React.RefObject<TElement>;

854

slots: Record<string, string>;

855

classNames: Record<string, string>;

856

[key: string]: any;

857

}

858

859

// Event handler types

860

type PressHandler = () => void;

861

type ValueChangeHandler<T> = (value: T) => void;

862

type SelectionChangeHandler = (keys: Selection) => void;

863

type OpenChangeHandler = (isOpen: boolean) => void;

864

```

865

866

## Integration Examples

867

868

### Custom Hook Composition

869

870

Combining multiple NextUI hooks for complex functionality.

871

872

```typescript

873

import {

874

useDisclosure, useScrollShadow, useRipple,

875

Card, CardHeader, CardBody, Button, Modal, ModalContent

876

} from "@nextui-org/react";

877

878

function useEnhancedCard() {

879

const modal = useDisclosure();

880

const { ripples, onClick } = useRipple({ color: "primary" });

881

const {

882

scrollRef,

883

isTopShadowVisible,

884

isBottomShadowVisible

885

} = useScrollShadow();

886

887

const cardProps = {

888

isPressable: true,

889

onClick: (e: React.MouseEvent) => {

890

onClick(e);

891

modal.onOpen();

892

},

893

className: "relative overflow-hidden",

894

};

895

896

return {

897

modal,

898

scrollRef,

899

isTopShadowVisible,

900

isBottomShadowVisible,

901

ripples,

902

cardProps,

903

};

904

}

905

906

function EnhancedCardExample() {

907

const {

908

modal,

909

scrollRef,

910

isTopShadowVisible,

911

isBottomShadowVisible,

912

ripples,

913

cardProps,

914

} = useEnhancedCard();

915

916

const items = Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`);

917

918

return (

919

<>

920

<Card {...cardProps}>

921

<CardHeader>

922

<h3>Enhanced Interactive Card</h3>

923

</CardHeader>

924

<CardBody className="relative">

925

{isTopShadowVisible && (

926

<div className="absolute top-0 left-0 right-0 h-4 bg-gradient-to-b from-background to-transparent z-10" />

927

)}

928

929

<div

930

ref={scrollRef}

931

className="max-h-32 overflow-y-auto space-y-2"

932

>

933

{items.map((item) => (

934

<div key={item} className="p-2 bg-default-100 rounded">

935

{item}

936

</div>

937

))}

938

</div>

939

940

{isBottomShadowVisible && (

941

<div className="absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-t from-background to-transparent z-10" />

942

)}

943

944

{ripples}

945

</CardBody>

946

</Card>

947

948

<Modal isOpen={modal.isOpen} onOpenChange={modal.onOpenChange}>

949

<ModalContent>

950

<div className="p-6">

951

<h2 className="text-xl font-bold mb-4">Card Details</h2>

952

<p>This modal was opened by clicking the card above!</p>

953

<Button

954

color="primary"

955

onPress={modal.onClose}

956

className="mt-4"

957

>

958

Close

959

</Button>

960

</div>

961

</ModalContent>

962

</Modal>

963

</>

964

);

965

}

966

```

967

968

### Advanced Component Extension

969

970

Creating sophisticated component extensions using NextUI utilities.

971

972

```typescript

973

import {

974

extendVariants, forwardRef, cn,

975

Button, Card, Badge

976

} from "@nextui-org/react";

977

978

// Extended button with notification capabilities

979

const NotificationButton = extendVariants(Button, {

980

variants: {

981

notification: {

982

true: "relative",

983

false: "",

984

},

985

notificationColor: {

986

primary: "",

987

danger: "",

988

warning: "",

989

success: "",

990

},

991

},

992

defaultVariants: {

993

notification: false,

994

notificationColor: "primary",

995

},

996

});

997

998

// Enhanced card with advanced features

999

interface EnhancedCardProps {

1000

children: React.ReactNode;

1001

isInteractive?: boolean;

1002

showRipple?: boolean;

1003

notification?: {

1004

count: number;

1005

color?: "primary" | "danger" | "warning" | "success";

1006

};

1007

className?: string;

1008

}

1009

1010

const EnhancedCard = forwardRef<HTMLDivElement, EnhancedCardProps>(

1011

({

1012

children,

1013

isInteractive = false,

1014

showRipple = false,

1015

notification,

1016

className,

1017

...props

1018

}, ref) => {

1019

const { ripples, onClick } = useRipple({

1020

color: "primary",

1021

isDisabled: !showRipple,

1022

});

1023

1024

return (

1025

<Card

1026

ref={ref}

1027

isPressable={isInteractive}

1028

className={cn(

1029

"relative transition-transform",

1030

isInteractive && "hover:scale-105",

1031

showRipple && "overflow-hidden",

1032

className

1033

)}

1034

onPress={showRipple ? onClick : undefined}

1035

{...props}

1036

>

1037

{children}

1038

{showRipple && ripples}

1039

{notification && (

1040

<Badge

1041

content={notification.count}

1042

color={notification.color}

1043

className="absolute -top-2 -right-2"

1044

/>

1045

)}

1046

</Card>

1047

);

1048

}

1049

);

1050

1051

function AdvancedComponentExample() {

1052

return (

1053

<div className="space-y-6">

1054

{/* Enhanced notification button */}

1055

<NotificationButton

1056

notification={true}

1057

notificationColor="danger"

1058

color="primary"

1059

>

1060

Messages

1061

<Badge

1062

content={5}

1063

color="danger"

1064

className="absolute -top-2 -right-2"

1065

/>

1066

</NotificationButton>

1067

1068

{/* Enhanced interactive card */}

1069

<EnhancedCard

1070

isInteractive

1071

showRipple

1072

notification={{ count: 3, color: "warning" }}

1073

className="w-64 h-32"

1074

>

1075

<div className="p-4">

1076

<h3 className="font-bold">Interactive Card</h3>

1077

<p className="text-sm text-default-500">

1078

Click for ripple effect

1079

</p>

1080

</div>

1081

</EnhancedCard>

1082

</div>

1083

);

1084

}

1085

```

1086

1087

### Resizable Panel

1088

1089

Advanced resizable panel component built with Framer Motion for creating flexible layout systems.

1090

1091

```typescript { .api }

1092

interface ResizablePanelProps {

1093

/** Panel content */

1094

children: React.ReactNode;

1095

/** Initial panel size */

1096

defaultSize?: number;

1097

/** Minimum panel size */

1098

minSize?: number;

1099

/** Maximum panel size */

1100

maxSize?: number;

1101

/** Resize direction */

1102

direction?: "horizontal" | "vertical";

1103

/** Whether resizing is disabled */

1104

isDisabled?: boolean;

1105

/** Panel identifier */

1106

id?: string;

1107

/** Custom CSS class */

1108

className?: string;

1109

/** Size change handler */

1110

onSizeChange?: (size: number) => void;

1111

/** Resize start handler */

1112

onResizeStart?: () => void;

1113

/** Resize end handler */

1114

onResizeEnd?: (size: number) => void;

1115

}

1116

1117

function ResizablePanel(props: ResizablePanelProps): JSX.Element;

1118

1119

/**

1120

* Hook for managing resizable panel groups

1121

*/

1122

function useResizablePanelGroup(): {

1123

registerPanel: (id: string, config: ResizablePanelConfig) => void;

1124

unregisterPanel: (id: string) => void;

1125

getPanel: (id: string) => ResizablePanelState | undefined;

1126

resizePanel: (id: string, size: number) => void;

1127

};

1128

1129

interface ResizablePanelConfig {

1130

defaultSize: number;

1131

minSize?: number;

1132

maxSize?: number;

1133

direction: "horizontal" | "vertical";

1134

}

1135

1136

interface ResizablePanelState {

1137

id: string;

1138

size: number;

1139

minSize: number;

1140

maxSize: number;

1141

isResizing: boolean;

1142

}

1143

```

1144

1145

**ResizablePanel Usage Example:**

1146

1147

```typescript

1148

import { ResizablePanel, Card, CardBody } from "@nextui-org/react";

1149

1150

function ResizablePanelExample() {

1151

const [leftSize, setLeftSize] = useState(300);

1152

const [rightSize, setRightSize] = useState(400);

1153

1154

return (

1155

<div className="flex h-96 border border-divider rounded-lg overflow-hidden">

1156

{/* Left panel */}

1157

<ResizablePanel

1158

defaultSize={leftSize}

1159

minSize={200}

1160

maxSize={500}

1161

direction="horizontal"

1162

onSizeChange={setLeftSize}

1163

className="border-r border-divider"

1164

>

1165

<Card className="h-full shadow-none">

1166

<CardBody>

1167

<h3 className="font-semibold mb-2">Left Panel</h3>

1168

<p className="text-sm text-default-500">

1169

This panel can be resized horizontally.

1170

Current width: {leftSize}px

1171

</p>

1172

<div className="mt-4 space-y-2">

1173

<div className="h-2 bg-primary rounded" />

1174

<div className="h-2 bg-secondary rounded" />

1175

<div className="h-2 bg-success rounded" />

1176

</div>

1177

</CardBody>

1178

</Card>

1179

</ResizablePanel>

1180

1181

{/* Center panel */}

1182

<div className="flex-1 border-r border-divider">

1183

<Card className="h-full shadow-none">

1184

<CardBody>

1185

<h3 className="font-semibold mb-2">Fixed Center</h3>

1186

<p className="text-sm text-default-500">

1187

This panel takes remaining space between the resizable panels.

1188

</p>

1189

</CardBody>

1190

</Card>

1191

</div>

1192

1193

{/* Right panel */}

1194

<ResizablePanel

1195

defaultSize={rightSize}

1196

minSize={250}

1197

maxSize={600}

1198

direction="horizontal"

1199

onSizeChange={setRightSize}

1200

>

1201

<Card className="h-full shadow-none">

1202

<CardBody>

1203

<h3 className="font-semibold mb-2">Right Panel</h3>

1204

<p className="text-sm text-default-500">

1205

Another resizable panel.

1206

Current width: {rightSize}px

1207

</p>

1208

<div className="mt-4">

1209

<div className="grid grid-cols-2 gap-2">

1210

<div className="h-8 bg-warning rounded" />

1211

<div className="h-8 bg-danger rounded" />

1212

<div className="h-8 bg-default rounded" />

1213

<div className="h-8 bg-foreground rounded" />

1214

</div>

1215

</div>

1216

</CardBody>

1217

</Card>

1218

</ResizablePanel>

1219

</div>

1220

);

1221

}

1222

```

1223

1224

## Additional Utility Exports

1225

1226

```typescript { .api }

1227

// Framer Motion utilities

1228

export { ResizablePanel } from "@nextui-org/framer-utils";

1229

1230

// React Aria utilities

1231

export { VisuallyHidden } from "@react-aria/visually-hidden";

1232

1233

// Date utilities

1234

export type { CalendarDate, CalendarDateTime, ZonedDateTime, Time } from "@internationalized/date";

1235

export {

1236

today,

1237

now,

1238

getLocalTimeZone,

1239

isWeekend,

1240

startOfWeek,

1241

endOfWeek,

1242

parseDate,

1243

parseDateTime,

1244

parseZonedDateTime,

1245

parseTime

1246

} from "@internationalized/date";

1247

```