Modal

Modal dialog for focusing user attention on important content.

import { Modal, Button } from "ui-lab-components";
import { useState } from "react";
 
export function Example() {
  const [isOpen, setIsOpen] = useState(false);
 
  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        Open Modal
      </Button>
      <Modal isOpen={isOpen} onOpenChange={setIsOpen}>
        <Modal.Header>Modal Title</Modal.Header>
        <Modal.Body>Modal content goes here</Modal.Body>
      </Modal>
    </>
  );
}

Basic Modal

A simple modal dialog with a trigger button. Use this for important user interactions that require focused attention.

'use client';
 
import React from 'react';
import { Modal, Button } from 'ui-lab-components';
 
export const metadata = {
  title: 'Basic Modal',
  description: 'A simple modal dialog with a trigger button. Use this for important user interactions that require focused attention.'
};
 
export default function Example() {
  const [isOpen, setIsOpen] = React.useState(false);
 
  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Open Modal</Button>
      <Modal isOpen={isOpen} onOpenChange={setIsOpen}>
        <Modal.Header>Modal Title</Modal.Header>
        <Modal.Body>This is the modal content. It displays important information or actions.</Modal.Body>
        <Modal.Footer>Modal Footer</Modal.Footer>
      </Modal>
    </>
  );
}
 

Form Modal

A modal dialog containing a form for editing user profile settings.

'use client';
 
import React from 'react';
import { Modal, Button, Input, Label, TextArea, Flex } from 'ui-lab-components';
 
export const metadata = {
  title: 'Form Modal',
  description: 'A modal dialog containing a form for editing user profile settings. Demonstrates using form inputs, labels, and action buttons within a modal.'
};
 
export default function Example() {
  const [isOpen, setIsOpen] = React.useState(false);
  const [formData, setFormData] = React.useState({
    name: 'John Doe',
    email: 'john.doe@example.com',
    bio: 'Software developer passionate about building great user experiences.',
  });
 
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // Handle form submission
    setIsOpen(false);
  };
 
  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Edit Profile</Button>
      <Modal isOpen={isOpen} onOpenChange={setIsOpen} size="auto">
        <Modal.Header>Edit Profile</Modal.Header>
        <Modal.Body>
          <form id="profile-form" onSubmit={handleSubmit}>
            <Flex direction="column" gap="md">
              <div>
                <Label htmlFor="name" required>
                  Full Name
                </Label>
                <Input
                  id="name"
                  value={formData.name}
                  onChange={(e) =>
                    setFormData({ ...formData, name: e.target.value })
                  }
                  placeholder="Enter your name"
                />
              </div>
              <div>
                <Label htmlFor="email" required>
                  Email Address
                </Label>
                <Input
                  id="email"
                  type="email"
                  value={formData.email}
                  onChange={(e) =>
                    setFormData({ ...formData, email: e.target.value })
                  }
                  placeholder="Enter your email"
                />
              </div>
              <div>
                <Label htmlFor="bio">Bio</Label>
                <TextArea
                  id="bio"
                  value={formData.bio}
                  onChange={(e) =>
                    setFormData({ ...formData, bio: e.target.value })
                  }
                  placeholder="Tell us about yourself"
                  rows={3}
                />
              </div>
            </Flex>
          </form>
        </Modal.Body>
        <Modal.Footer>
          <Flex gap="sm" justify="end">
            <Button variant="ghost" onClick={() => setIsOpen(false)}>
              Cancel
            </Button>
            <Button type="submit" form="profile-form">
              Save Changes
            </Button>
          </Flex>
        </Modal.Footer>
      </Modal>
    </>
  );
}
 

Delete Confirmation

Destructive action dialog that blocks the user until they explicitly confirm or cancel.

"use client";
 
import { useState } from "react";
import { Modal, Button } from "ui-lab-components";
 
export const metadata = {
  title: "Delete Confirmation",
  description: "Destructive action dialog that blocks the user until they explicitly confirm or cancel.",
};
 
export default function Example() {
  const [isOpen, setIsOpen] = useState(false);
 
  return (
    <>
      <Button variant="destructive" onClick={() => setIsOpen(true)}>
        Delete workspace
      </Button>
 
      <Modal
        isOpen={isOpen}
        onOpenChange={setIsOpen}
        title="Delete workspace"
        footer={
          <>
            <Button variant="ghost" onClick={() => setIsOpen(false)}>
              Cancel
            </Button>
            <Button variant="destructive" onClick={() => setIsOpen(false)}>
              Delete
            </Button>
          </>
        }
      >
        <p className="text-sm text-foreground-300">
          This will permanently delete <strong className="text-foreground-100">acme-corp</strong> and all
          its data. This action cannot be undone.
        </p>
      </Modal>
    </>
  );
}
 

Create API Key

Form modal with a single required input. The primary action stays disabled until the field has a value.

"use client";
 
import { useState } from "react";
import { Modal, Button, Input, Label } from "ui-lab-components";
 
export const metadata = {
  title: "Create API Key",
  description: "Form modal with a single required input. The primary action stays disabled until the field has a value.",
};
 
export default function Example() {
  const [isOpen, setIsOpen] = useState(false);
  const [name, setName] = useState("");
 
  return (
    <>
      <Button onClick={() => setIsOpen(true)}>New API key</Button>
 
      <Modal
        isOpen={isOpen}
        onOpenChange={setIsOpen}
        title="New API key"
        footer={
          <>
            <Button variant="ghost" onClick={() => setIsOpen(false)}>
              Cancel
            </Button>
            <Button
              onClick={() => setIsOpen(false)}
              isDisabled={!name.trim()}
            >
              Create
            </Button>
          </>
        }
      >
        <div className="flex flex-col gap-4 px-6 py-4">
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="key-name">Name</Label>
            <Input
              id="key-name"
              placeholder="e.g. CI deploy key"
              value={name}
              onChange={(e) => setName(e.target.value)}
            />
          </div>
          <p className="text-xs text-foreground-400">
            The key will only be shown once after creation.
          </p>
        </div>
      </Modal>
    </>
  );
}
 

Notification Settings

Settings panel using the compound Modal.Header / Modal.Body / Modal.Footer API with toggle rows.

"use client";
 
import { useState } from "react";
import { Modal, Button, Switch } from "ui-lab-components";
 
export const metadata = {
  title: "Notification Settings",
  description: "Settings panel using the compound Modal.Header / Modal.Body / Modal.Footer API with toggle rows.",
};
 
export default function Example() {
  const [isOpen, setIsOpen] = useState(false);
  const [prefs, setPrefs] = useState({ email: true, push: false, marketing: false });
 
  const toggle = (key: keyof typeof prefs) =>
    setPrefs((p) => ({ ...p, [key]: !p[key] }));
 
  return (
    <>
      <Button variant="ghost" onClick={() => setIsOpen(true)}>
        Notification settings
      </Button>
 
      <Modal isOpen={isOpen} onOpenChange={setIsOpen}>
        <Modal.Header>
          <span className="text-sm font-semibold text-foreground-100">Notification preferences</span>
        </Modal.Header>
 
        <Modal.Body>
          <div className="flex flex-col divide-y divide-border px-6">
            {(
              [
                { key: "email", label: "Email notifications", description: "Receive updates and alerts by email" },
                { key: "push", label: "Push notifications", description: "Browser and mobile push alerts" },
                { key: "marketing", label: "Product updates", description: "New features and announcements" },
              ] as const
            ).map(({ key, label, description }) => (
              <div key={key} className="flex items-center justify-between py-4">
                <div className="flex flex-col gap-0.5">
                  <span className="text-sm text-foreground-100">{label}</span>
                  <span className="text-xs text-foreground-400">{description}</span>
                </div>
                <Switch isSelected={prefs[key]} onChange={() => toggle(key)} />
              </div>
            ))}
          </div>
        </Modal.Body>
 
        <Modal.Footer>
          <Button variant="ghost" onClick={() => setIsOpen(false)}>
            Cancel
          </Button>
          <Button onClick={() => setIsOpen(false)}>Save</Button>
        </Modal.Footer>
      </Modal>
    </>
  );
}
 
UI Lab