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
```