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

feedback.mddocs/

0

# Feedback Components

1

2

NextUI provides comprehensive feedback components for communicating system status, user notifications, loading states, and contextual information to enhance user experience.

3

4

## Capabilities

5

6

### Alert

7

8

Notification component for displaying important messages, warnings, and status information with customizable styling and actions.

9

10

```typescript { .api }

11

interface AlertProps {

12

/** Alert content */

13

children?: React.ReactNode;

14

/** Alert title */

15

title?: React.ReactNode;

16

/** Alert description */

17

description?: React.ReactNode;

18

/** Alert color theme */

19

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

20

/** Alert variant */

21

variant?: "solid" | "bordered" | "light" | "flat" | "faded";

22

/** Border radius */

23

radius?: "none" | "sm" | "md" | "lg" | "full";

24

/** Start content (icon, etc.) */

25

startContent?: React.ReactNode;

26

/** End content (action buttons, etc.) */

27

endContent?: React.ReactNode;

28

/** Whether alert is closeable */

29

isClosable?: boolean;

30

/** Whether alert is visible */

31

isVisible?: boolean;

32

/** Default visibility state */

33

defaultVisible?: boolean;

34

/** Hide icon */

35

hideIconWrapper?: boolean;

36

/** Custom CSS class */

37

className?: string;

38

/** Slot-based styling */

39

classNames?: SlotsToClasses<AlertSlots>;

40

/** Close event handler */

41

onClose?: () => void;

42

/** Visibility change handler */

43

onVisibilityChange?: (isVisible: boolean) => void;

44

}

45

46

type AlertSlots =

47

| "base" | "wrapper" | "iconWrapper" | "icon"

48

| "mainWrapper" | "title" | "description"

49

| "closeButton" | "closeIcon";

50

51

function Alert(props: AlertProps): JSX.Element;

52

53

/**

54

* Hook for Alert state management

55

*/

56

function useAlert(props: AlertProps): {

57

Component: React.ElementType;

58

slots: Record<AlertSlots, string>;

59

classNames: SlotsToClasses<AlertSlots>;

60

isVisible: boolean;

61

getAlertProps: () => any;

62

getWrapperProps: () => any;

63

getIconWrapperProps: () => any;

64

getMainWrapperProps: () => any;

65

getTitleProps: () => any;

66

getDescriptionProps: () => any;

67

getCloseButtonProps: () => any;

68

};

69

```

70

71

**Alert Usage Examples:**

72

73

```typescript

74

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

75

import { InfoIcon, CheckIcon, WarningIcon } from "@heroicons/react/24/solid";

76

77

function AlertExamples() {

78

const [isVisible, setIsVisible] = useState(true);

79

80

return (

81

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

82

{/* Basic alerts */}

83

<Alert color="primary" title="Info Alert">

84

This is an informational message.

85

</Alert>

86

87

<Alert

88

color="success"

89

variant="bordered"

90

startContent={<CheckIcon className="w-5 h-5" />}

91

title="Success!"

92

description="Your action was completed successfully."

93

/>

94

95

<Alert

96

color="warning"

97

variant="flat"

98

startContent={<WarningIcon className="w-5 h-5" />}

99

title="Warning"

100

description="Please review the following items before continuing."

101

/>

102

103

<Alert

104

color="danger"

105

variant="light"

106

title="Error"

107

description="Something went wrong. Please try again."

108

endContent={

109

<Button color="danger" size="sm" variant="flat">

110

Retry

111

</Button>

112

}

113

/>

114

115

{/* Closeable alert */}

116

<Alert

117

color="secondary"

118

title="Dismissible Alert"

119

description="This alert can be closed by clicking the X button."

120

isClosable

121

isVisible={isVisible}

122

onVisibilityChange={setIsVisible}

123

/>

124

</div>

125

);

126

}

127

```

128

129

### Progress

130

131

Linear progress bar component for displaying completion status with customizable appearance and value formatting.

132

133

```typescript { .api }

134

interface ProgressProps {

135

/** Progress label */

136

label?: React.ReactNode;

137

/** Progress size */

138

size?: "sm" | "md" | "lg";

139

/** Border radius */

140

radius?: "none" | "sm" | "md" | "lg" | "full";

141

/** Color theme */

142

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

143

/** Current progress value (0-100) */

144

value?: number;

145

/** Minimum value */

146

minValue?: number;

147

/** Maximum value */

148

maxValue?: number;

149

/** Indeterminate loading state */

150

isIndeterminate?: boolean;

151

/** Show value label */

152

showValueLabel?: boolean;

153

/** Custom value label */

154

valueLabel?: React.ReactNode;

155

/** Number formatting options */

156

formatOptions?: Intl.NumberFormatOptions;

157

/** Disabled state */

158

isDisabled?: boolean;

159

/** Disable animations */

160

disableAnimation?: boolean;

161

/** Custom CSS class */

162

className?: string;

163

/** Slot-based styling */

164

classNames?: SlotsToClasses<ProgressSlots>;

165

/** Value change handler */

166

onValueChange?: (value: number) => void;

167

}

168

169

type ProgressSlots = "base" | "labelWrapper" | "label" | "value" | "track" | "indicator";

170

171

function Progress(props: ProgressProps): JSX.Element;

172

173

/**

174

* Hook for Progress state management

175

*/

176

function useProgress(props: ProgressProps): {

177

Component: React.ElementType;

178

slots: Record<ProgressSlots, string>;

179

classNames: SlotsToClasses<ProgressSlots>;

180

progressBarProps: any;

181

labelProps: any;

182

percentage: number;

183

getProgressBarProps: () => any;

184

getLabelProps: () => any;

185

};

186

```

187

188

### Circular Progress

189

190

Circular progress indicator for compact loading states and completion visualization.

191

192

```typescript { .api }

193

interface CircularProgressProps {

194

/** Progress label */

195

label?: React.ReactNode;

196

/** Progress size */

197

size?: "sm" | "md" | "lg";

198

/** Color theme */

199

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

200

/** Current progress value (0-100) */

201

value?: number;

202

/** Minimum value */

203

minValue?: number;

204

/** Maximum value */

205

maxValue?: number;

206

/** Indeterminate loading state */

207

isIndeterminate?: boolean;

208

/** Show value label */

209

showValueLabel?: boolean;

210

/** Custom value label */

211

valueLabel?: React.ReactNode;

212

/** Number formatting options */

213

formatOptions?: Intl.NumberFormatOptions;

214

/** Disabled state */

215

isDisabled?: boolean;

216

/** Disable animations */

217

disableAnimation?: boolean;

218

/** Stroke width */

219

strokeWidth?: number;

220

/** Custom CSS class */

221

className?: string;

222

/** Slot-based styling */

223

classNames?: SlotsToClasses<CircularProgressSlots>;

224

/** Value change handler */

225

onValueChange?: (value: number) => void;

226

}

227

228

type CircularProgressSlots =

229

| "base" | "svgWrapper" | "svg" | "track" | "indicator"

230

| "value" | "label";

231

232

function CircularProgress(props: CircularProgressProps): JSX.Element;

233

```

234

235

**Progress Usage Examples:**

236

237

```typescript

238

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

239

240

function ProgressExamples() {

241

const [value, setValue] = useState(0);

242

243

useEffect(() => {

244

const interval = setInterval(() => {

245

setValue((v) => (v >= 100 ? 0 : v + 10));

246

}, 500);

247

248

return () => clearInterval(interval);

249

}, []);

250

251

return (

252

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

253

{/* Linear progress */}

254

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

255

<Progress

256

aria-label="Loading..."

257

size="md"

258

value={value}

259

color="success"

260

showValueLabel={true}

261

className="max-w-md"

262

/>

263

264

<Progress

265

label="Loading files..."

266

size="sm"

267

isIndeterminate

268

color="primary"

269

className="max-w-md"

270

/>

271

272

<Progress

273

size="lg"

274

radius="sm"

275

classNames={{

276

base: "max-w-md",

277

track: "drop-shadow-md border border-default",

278

indicator: "bg-gradient-to-r from-pink-500 to-yellow-500",

279

label: "tracking-wider font-medium text-default-600",

280

value: "text-foreground/60",

281

}}

282

label="Custom styled progress"

283

value={65}

284

showValueLabel={true}

285

/>

286

</div>

287

288

{/* Circular progress */}

289

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

290

<CircularProgress

291

aria-label="Loading..."

292

size="lg"

293

value={value}

294

color="warning"

295

showValueLabel={true}

296

/>

297

298

<CircularProgress

299

label="CPU Usage"

300

size="lg"

301

value={70}

302

color="danger"

303

formatOptions={{ style: "unit", unit: "percent" }}

304

showValueLabel={true}

305

/>

306

307

<CircularProgress

308

size="sm"

309

isIndeterminate

310

color="secondary"

311

aria-label="Loading..."

312

/>

313

</div>

314

</div>

315

);

316

}

317

```

318

319

### Spinner

320

321

Loading spinner component for indicating processing states with various sizes and colors.

322

323

```typescript { .api }

324

interface SpinnerProps {

325

/** Spinner label */

326

label?: React.ReactNode;

327

/** Spinner size */

328

size?: "sm" | "md" | "lg";

329

/** Spinner color */

330

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

331

/** Label color */

332

labelColor?: "foreground" | "primary" | "secondary" | "success" | "warning" | "danger";

333

/** Custom CSS class */

334

className?: string;

335

/** Slot-based styling */

336

classNames?: SlotsToClasses<SpinnerSlots>;

337

}

338

339

type SpinnerSlots = "base" | "wrapper" | "circle1" | "circle2" | "label";

340

341

function Spinner(props: SpinnerProps): JSX.Element;

342

343

/**

344

* Hook for Spinner state management

345

*/

346

function useSpinner(props: SpinnerProps): {

347

Component: React.ElementType;

348

slots: Record<SpinnerSlots, string>;

349

classNames: SlotsToClasses<SpinnerSlots>;

350

getSpinnerProps: () => any;

351

getWrapperProps: () => any;

352

getLabelProps: () => any;

353

};

354

```

355

356

**Spinner Usage Examples:**

357

358

```typescript

359

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

360

361

function SpinnerExamples() {

362

return (

363

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

364

{/* Basic spinners */}

365

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

366

<Spinner size="sm" />

367

<Spinner size="md" />

368

<Spinner size="lg" />

369

</div>

370

371

{/* Colored spinners */}

372

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

373

<Spinner color="primary" />

374

<Spinner color="secondary" />

375

<Spinner color="success" />

376

<Spinner color="warning" />

377

<Spinner color="danger" />

378

</div>

379

380

{/* Spinner with labels */}

381

<div className="flex flex-col gap-4">

382

<Spinner label="Loading..." />

383

384

<Spinner

385

label="Processing your request"

386

color="warning"

387

labelColor="warning"

388

/>

389

</div>

390

391

{/* Spinner in content */}

392

<Card>

393

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

394

<Spinner

395

size="lg"

396

label="Loading content..."

397

color="primary"

398

/>

399

</CardBody>

400

</Card>

401

</div>

402

);

403

}

404

```

405

406

### Skeleton

407

408

Placeholder loading component that mimics content structure during data loading.

409

410

```typescript { .api }

411

interface SkeletonProps {

412

/** Skeleton content */

413

children?: React.ReactNode;

414

/** Whether skeleton is loading */

415

isLoaded?: boolean;

416

/** Disable animations */

417

disableAnimation?: boolean;

418

/** Custom CSS class */

419

className?: string;

420

}

421

422

function Skeleton(props: SkeletonProps): JSX.Element;

423

424

/**

425

* Hook for Skeleton state management

426

*/

427

function useSkeleton(props: SkeletonProps): {

428

Component: React.ElementType;

429

getSkeletonProps: () => any;

430

};

431

```

432

433

**Skeleton Usage Examples:**

434

435

```typescript

436

import { Skeleton, Card, CardHeader, CardBody, Avatar } from "@nextui-org/react";

437

438

function SkeletonExamples() {

439

const [isLoaded, setIsLoaded] = useState(false);

440

441

useEffect(() => {

442

const timer = setTimeout(() => setIsLoaded(true), 3000);

443

return () => clearTimeout(timer);

444

}, []);

445

446

return (

447

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

448

{/* Content skeleton */}

449

<Card className="w-[200px] space-y-5 p-4" radius="lg">

450

<Skeleton isLoaded={isLoaded} className="rounded-lg">

451

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

452

</Skeleton>

453

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

454

<Skeleton isLoaded={isLoaded} className="w-3/5 rounded-lg">

455

<div className="h-3 w-full rounded-lg bg-secondary"></div>

456

</Skeleton>

457

<Skeleton isLoaded={isLoaded} className="w-4/5 rounded-lg">

458

<div className="h-3 w-full rounded-lg bg-secondary-300"></div>

459

</Skeleton>

460

<Skeleton isLoaded={isLoaded} className="w-2/5 rounded-lg">

461

<div className="h-3 w-full rounded-lg bg-secondary-200"></div>

462

</Skeleton>

463

</div>

464

</Card>

465

466

{/* User profile skeleton */}

467

<Card className="max-w-[300px]">

468

<CardHeader className="justify-between">

469

<div className="flex gap-5">

470

<Skeleton isLoaded={isLoaded} className="flex rounded-full w-12 h-12">

471

<Avatar

472

isBordered

473

radius="full"

474

size="md"

475

src="https://nextui.org/avatars/avatar-1.png"

476

/>

477

</Skeleton>

478

<div className="flex flex-col gap-1 items-start justify-center">

479

<Skeleton isLoaded={isLoaded} className="w-20 rounded-lg">

480

<h4 className="text-small font-semibold leading-none text-default-600">

481

Zoey Lang

482

</h4>

483

</Skeleton>

484

<Skeleton isLoaded={isLoaded} className="w-16 rounded-lg">

485

<h5 className="text-small tracking-tight text-default-400">

486

@zoeylang

487

</h5>

488

</Skeleton>

489

</div>

490

</div>

491

<Skeleton isLoaded={isLoaded} className="rounded-lg">

492

<Button color="primary" radius="full" size="sm">

493

Follow

494

</Button>

495

</Skeleton>

496

</CardHeader>

497

<CardBody className="px-3 py-0 text-small text-default-400">

498

<Skeleton isLoaded={isLoaded} className="rounded-lg">

499

<p>

500

Frontend developer and UI/UX enthusiast. Join me on this coding adventure!

501

</p>

502

</Skeleton>

503

</CardBody>

504

</Card>

505

</div>

506

);

507

}

508

```

509

510

### Tooltip

511

512

Contextual information overlay that appears on hover or focus to provide additional details.

513

514

```typescript { .api }

515

interface TooltipProps {

516

/** Target element to attach tooltip to */

517

children: React.ReactElement;

518

/** Tooltip content */

519

content?: React.ReactNode;

520

/** Whether tooltip is open */

521

isOpen?: boolean;

522

/** Default open state */

523

defaultOpen?: boolean;

524

/** Tooltip placement */

525

placement?: Placement;

526

/** Show delay in milliseconds */

527

delay?: number;

528

/** Close delay in milliseconds */

529

closeDelay?: number;

530

/** Disabled state */

531

isDisabled?: boolean;

532

/** Whether tooltip should flip to fit */

533

shouldFlip?: boolean;

534

/** Container padding for flip calculations */

535

containerPadding?: number;

536

/** Offset from target */

537

offset?: number;

538

/** Cross-axis offset */

539

crossOffset?: number;

540

/** Show arrow pointer */

541

showArrow?: boolean;

542

/** Border radius */

543

radius?: "none" | "sm" | "md" | "lg" | "full";

544

/** Tooltip size */

545

size?: "sm" | "md" | "lg";

546

/** Color theme */

547

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

548

/** Motion configuration */

549

motionProps?: MotionProps;

550

/** Custom CSS class */

551

className?: string;

552

/** Slot-based styling */

553

classNames?: SlotsToClasses<TooltipSlots>;

554

/** Open change handler */

555

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

556

}

557

558

type TooltipSlots = "base" | "arrow" | "content";

559

560

type Placement =

561

| "top" | "top-start" | "top-end"

562

| "bottom" | "bottom-start" | "bottom-end"

563

| "right" | "right-start" | "right-end"

564

| "left" | "left-start" | "left-end";

565

566

function Tooltip(props: TooltipProps): JSX.Element;

567

568

/**

569

* Hook for Tooltip state management

570

*/

571

function useTooltip(props: TooltipProps): {

572

Component: React.ElementType;

573

isOpen: boolean;

574

disableAnimation: boolean;

575

getTooltipProps: () => any;

576

getTriggerProps: () => any;

577

getTooltipContentProps: () => any;

578

};

579

```

580

581

**Tooltip Usage Examples:**

582

583

```typescript

584

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

585

import { DeleteIcon, EditIcon, EyeIcon } from "@heroicons/react/24/solid";

586

587

function TooltipExamples() {

588

return (

589

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

590

{/* Basic tooltips */}

591

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

592

<Tooltip content="I am a tooltip">

593

<Button>Hover me</Button>

594

</Tooltip>

595

596

<Tooltip content="Tooltip with arrow" showArrow>

597

<Button color="primary">With Arrow</Button>

598

</Tooltip>

599

</div>

600

601

{/* Colored tooltips */}

602

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

603

<Tooltip content="Edit" color="success" showArrow>

604

<Button isIconOnly color="success" variant="light">

605

<EditIcon className="w-4 h-4" />

606

</Button>

607

</Tooltip>

608

609

<Tooltip content="View" color="secondary" showArrow>

610

<Button isIconOnly color="secondary" variant="light">

611

<EyeIcon className="w-4 h-4" />

612

</Button>

613

</Tooltip>

614

615

<Tooltip content="Delete" color="danger" showArrow>

616

<Button isIconOnly color="danger" variant="light">

617

<DeleteIcon className="w-4 h-4" />

618

</Button>

619

</Tooltip>

620

</div>

621

622

{/* Placement variations */}

623

<div className="flex flex-wrap gap-4">

624

{["top", "bottom", "left", "right"].map((placement) => (

625

<Tooltip

626

key={placement}

627

content={`Tooltip on ${placement}`}

628

placement={placement as Placement}

629

showArrow

630

>

631

<Button variant="bordered" className="capitalize">

632

{placement}

633

</Button>

634

</Tooltip>

635

))}

636

</div>

637

638

{/* Custom content tooltip */}

639

<Tooltip

640

showArrow

641

content={

642

<div className="px-1 py-2">

643

<div className="text-small font-bold">Custom Content</div>

644

<div className="text-tiny">This tooltip has custom styling</div>

645

</div>

646

}

647

>

648

<Button color="secondary" variant="flat">

649

Custom Content

650

</Button>

651

</Tooltip>

652

</div>

653

);

654

}

655

```

656

657

## Feedback Component Types

658

659

```typescript { .api }

660

// Common feedback types

661

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

662

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

663

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

664

665

// Alert types

666

interface AlertState {

667

isVisible: boolean;

668

isClosable: boolean;

669

color: FeedbackColor;

670

variant: "solid" | "bordered" | "light" | "flat" | "faded";

671

}

672

673

// Progress types

674

interface ProgressState {

675

value: number;

676

minValue: number;

677

maxValue: number;

678

percentage: number;

679

isIndeterminate: boolean;

680

valueLabel: string;

681

}

682

683

// Tooltip types

684

interface TooltipState {

685

isOpen: boolean;

686

placement: Placement;

687

isDisabled: boolean;

688

showArrow: boolean;

689

delay: number;

690

closeDelay: number;

691

}

692

693

// Motion configuration for animations

694

interface MotionProps {

695

initial?: any;

696

animate?: any;

697

exit?: any;

698

transition?: any;

699

variants?: any;

700

whileHover?: any;

701

whileTap?: any;

702

whileFocus?: any;

703

whileInView?: any;

704

}

705

706

// Placement type for positioning

707

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

708

type PlacementAlign = "start" | "end";

709

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

710

711

// Number formatting for progress values

712

interface ProgressFormatOptions extends Intl.NumberFormatOptions {

713

style?: "decimal" | "currency" | "percent" | "unit";

714

unit?: string;

715

currency?: string;

716

minimumFractionDigits?: number;

717

maximumFractionDigits?: number;

718

}

719

```

720

721

## Integration Examples

722

723

### Loading States Pattern

724

725

```typescript

726

import {

727

Skeleton, Spinner, Progress, Alert, Button, Card, CardBody

728

} from "@nextui-org/react";

729

730

function LoadingStatesExample() {

731

const [loadingState, setLoadingState] = useState<'idle' | 'loading' | 'error' | 'success'>('idle');

732

const [progress, setProgress] = useState(0);

733

734

const handleAction = async () => {

735

setLoadingState('loading');

736

setProgress(0);

737

738

try {

739

// Simulate progress updates

740

for (let i = 0; i <= 100; i += 10) {

741

setProgress(i);

742

await new Promise(resolve => setTimeout(resolve, 200));

743

}

744

745

setLoadingState('success');

746

} catch (error) {

747

setLoadingState('error');

748

}

749

};

750

751

return (

752

<div className="space-y-6 max-w-md">

753

{loadingState === 'loading' && (

754

<Card>

755

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

756

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

757

<Spinner size="sm" color="primary" />

758

<span>Processing...</span>

759

</div>

760

<Progress

761

value={progress}

762

color="primary"

763

showValueLabel

764

/>

765

</CardBody>

766

</Card>

767

)}

768

769

{loadingState === 'success' && (

770

<Alert

771

color="success"

772

title="Success!"

773

description="Your action was completed successfully."

774

/>

775

)}

776

777

{loadingState === 'error' && (

778

<Alert

779

color="danger"

780

title="Error"

781

description="Something went wrong. Please try again."

782

endContent={

783

<Button

784

color="danger"

785

variant="flat"

786

size="sm"

787

onPress={() => setLoadingState('idle')}

788

>

789

Retry

790

</Button>

791

}

792

/>

793

)}

794

795

<Button

796

color="primary"

797

onPress={handleAction}

798

isDisabled={loadingState === 'loading'}

799

>

800

{loadingState === 'loading' ? 'Processing...' : 'Start Action'}

801

</Button>

802

</div>

803

);

804

}

805

```

806

807

### Notification System

808

809

```typescript

810

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

811

812

function NotificationSystem() {

813

const [notifications, setNotifications] = useState<Array<{

814

id: string;

815

type: 'success' | 'warning' | 'danger' | 'info';

816

title: string;

817

message: string;

818

}>>([]);

819

820

const addNotification = (type: string, title: string, message: string) => {

821

const notification = {

822

id: Date.now().toString(),

823

type,

824

title,

825

message,

826

};

827

setNotifications(prev => [...prev, notification]);

828

829

// Auto remove after 5 seconds

830

setTimeout(() => {

831

removeNotification(notification.id);

832

}, 5000);

833

};

834

835

const removeNotification = (id: string) => {

836

setNotifications(prev => prev.filter(n => n.id !== id));

837

};

838

839

return (

840

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

841

{/* Notification triggers */}

842

<div className="flex gap-2">

843

<Button

844

color="success"

845

onPress={() => addNotification('success', 'Success', 'Operation completed successfully!')}

846

>

847

Success

848

</Button>

849

<Button

850

color="warning"

851

onPress={() => addNotification('warning', 'Warning', 'Please review your input.')}

852

>

853

Warning

854

</Button>

855

<Button

856

color="danger"

857

onPress={() => addNotification('danger', 'Error', 'Something went wrong.')}

858

>

859

Error

860

</Button>

861

</div>

862

863

{/* Notifications display */}

864

<div className="fixed top-4 right-4 space-y-2 z-50">

865

{notifications.map((notification) => (

866

<Alert

867

key={notification.id}

868

color={notification.type}

869

title={notification.title}

870

description={notification.message}

871

isClosable

872

onClose={() => removeNotification(notification.id)}

873

className="max-w-sm"

874

/>

875

))}

876

</div>

877

</div>

878

);

879

}

880

```