DEVELOPMENT PREVIEWIn development

Modal

Flexible modal dialogs with multiple patterns and state management options.

Basic Modal

Basic Modal Usage
import { Modal, Button } from '@creo-team/buzz-ui'
import { useState } from 'react'

export default function BasicExample() {
  const [open, setOpen] = useState(false)
  
  return (
    <>
      <Button onClick={() => setOpen(true)}>Open Modal</Button>
      
      <Modal 
        isOpen={open} 
        onClose={() => setOpen(false)} 
        header="Basic Modal"
      >
        <p>This is a basic modal with header and close functionality.</p>
      </Modal>
    </>
  )
}

Query Parameter State Management

πŸ’‘ Recommended Pattern

Using query parameters for modal state allows for shareable URLs, browser back/forward navigation, and better user experience.

Check the URL when opened!
Query Parameter Modal
import { Modal, Button } from '@creo-team/buzz-ui'
import { useSearchParams, useRouter } from 'next/navigation'

export default function QueryParamModal() {
  const searchParams = useSearchParams()
  const router = useRouter()
  const [isClient, setIsClient] = useState(false)
  
  // Prevent SSR issues
  useEffect(() => {
    setIsClient(true)
  }, [])
  
  const isOpen = isClient && searchParams.get('modal') === 'settings'
  
  const openModal = () => {
    if (!isClient) return
    const params = new URLSearchParams(searchParams.toString())
    params.set('modal', 'settings')
    router.push(`?${params.toString()}`)
  }
  
  const closeModal = () => {
    if (!isClient) return
    const params = new URLSearchParams(searchParams.toString())
    params.delete('modal')
    router.push(`?${params.toString()}`)
  }
  
  return (
    <>
      <Button onClick={openModal}>Open Settings</Button>
      
      <Modal 
        isOpen={isOpen} 
        onClose={closeModal} 
        header="Settings"
      >
        <div className="space-y-4">
          <p>Settings content here...</p>
          <p className="text-sm text-[var(--c-text-secondary)]">
            πŸ’‘ Notice the URL changes and browser back button works!
          </p>
        </div>
      </Modal>
    </>
  )
}

Form Modal

Form Modal Example
import { Modal, Button } from '@creo-team/buzz-ui'
import { useState } from 'react'

export default function FormModal() {
  const [open, setOpen] = useState(false)
  const [formData, setFormData] = useState({ name: '', email: '' })
  
  const handleSubmit = (e) => {
    e.preventDefault()
    console.log('Form submitted:', formData)
    setOpen(false)
  }
  
  return (
    <>
      <Button onClick={() => setOpen(true)}>Create User</Button>
      
      <Modal 
        isOpen={open} 
        onClose={() => setOpen(false)} 
        header="Create New User"
      >
        <form onSubmit={handleSubmit} className="space-y-4">
          <div>
            <label className="block text-sm font-medium mb-2">Name</label>
            <input
              type="text"
              value={formData.name}
              onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
              className="w-full p-2 border rounded"
              required
            />
          </div>
          
          <div>
            <label className="block text-sm font-medium mb-2">Email</label>
            <input
              type="email"
              value={formData.email}
              onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
              className="w-full p-2 border rounded"
              required
            />
          </div>
          
          <div className="flex gap-2 pt-4">
            <Button type="submit" variant="bold">Create</Button>
            <Button type="button" onClick={() => setOpen(false)}>Cancel</Button>
          </div>
        </form>
      </Modal>
    </>
  )
}

Confirmation Modal

Confirmation Modal
import { Modal, Button } from '@creo-team/buzz-ui'
import { useState } from 'react'

export default function ConfirmationModal() {
  const [open, setOpen] = useState(false)
  
  const handleDelete = () => {
    // Perform delete action
    console.log('Item deleted')
    setOpen(false)
  }
  
  return (
    <>
      <Button onClick={() => setOpen(true)} variant="danger">
        Delete Item
      </Button>
      
      <Modal 
        isOpen={open} 
        onClose={() => setOpen(false)} 
        header="Confirm Deletion"
      >
        <div className="space-y-4">
          <p>Are you sure you want to delete this item? This action cannot be undone.</p>
          
          <div className="flex gap-2 pt-4">
            <Button onClick={handleDelete} variant="danger">
              Delete
            </Button>
            <Button onClick={() => setOpen(false)}>
              Cancel
            </Button>
          </div>
        </div>
      </Modal>
    </>
  )
}

Advanced Patterns

Custom Hook for Query Params

Custom Hook Pattern
// Now available as a built-in hook!
import { useModalQuery } from '@creo-team/buzz-ui'

export default function MyComponent() {
  const settingsModal = useModalQuery('settings')
  const createModal = useModalQuery('create')
  
  return (
    <>
      <Button onClick={settingsModal.open}>Settings</Button>
      <Button onClick={createModal.open}>Create</Button>
      
      <Modal isOpen={settingsModal.isOpen} onClose={settingsModal.close}>
        Settings content
      </Modal>
      
      <Modal isOpen={createModal.isOpen} onClose={createModal.close}>
        Create form
      </Modal>
    </>
  )
}

// The hook handles SSR safety automatically:
// - Prevents "document is not defined" errors
// - Only accesses query params after client hydration
// - Provides clean open/close API

Multiple Modal Management

Multiple Modal Manager
// For managing multiple modals with query params
const MODAL_KEYS = {
  SETTINGS: 'settings',
  CREATE_USER: 'create-user',
  EDIT_PROFILE: 'edit-profile',
  CONFIRM_DELETE: 'confirm-delete'
} as const

export function useModals() {
  const searchParams = useSearchParams()
  const router = useRouter()
  
  const currentModal = searchParams.get('modal')
  
  const openModal = (modalKey: string) => {
    const params = new URLSearchParams(searchParams.toString())
    params.set('modal', modalKey)
    router.push(`?${params.toString()}`)
  }
  
  const closeModal = () => {
    const params = new URLSearchParams(searchParams.toString())
    params.delete('modal')
    router.push(`?${params.toString()}`)
  }
  
  return {
    currentModal,
    openModal,
    closeModal,
    isOpen: (modalKey: string) => currentModal === modalKey
  }
}

Modal with Data Loading

Data Loading Modal
import { Modal, Button } from '@creo-team/buzz-ui'
import { useState, useEffect } from 'react'

export default function DataModal() {
  const [open, setOpen] = useState(false)
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)
  
  useEffect(() => {
    if (open) {
      setLoading(true)
      // Load data when modal opens
      fetch('/api/user-data')
        .then(res => res.json())
        .then(setData)
        .finally(() => setLoading(false))
    }
  }, [open])
  
  return (
    <>
      <Button onClick={() => setOpen(true)}>View Profile</Button>
      
      <Modal isOpen={open} onClose={() => setOpen(false)} header="User Profile">
        {loading ? (
          <div className="flex items-center justify-center py-8">
            <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
          </div>
        ) : (
          <div className="space-y-4">
            <h3>Welcome, {data?.name}</h3>
            <p>Email: {data?.email}</p>
          </div>
        )}
			</Modal>
    </>
  )
}

API Reference

PropTypeDefaultDescription
isOpen*
booleanfalseControls modal visibility
onClose*
() => voidβ€”Called when modal should close
header
ReactNodeβ€”Optional header content
children
ReactNodeβ€”Modal body content
className
stringβ€”Additional CSS classes
closeOnBackdrop
booleantrueClose when clicking backdrop

Best Practices

βœ… Do

  • β€’ Use query params for shareable modal states
  • β€’ Provide clear close affordances
  • β€’ Keep modal content focused and concise
  • β€’ Use appropriate modal sizes
  • β€’ Handle loading states in data modals

❌ Don't

  • β€’ Nest modals inside other modals
  • β€’ Make modals too large or complex
  • β€’ Forget to handle escape key
  • β€’ Use modals for simple confirmations
  • β€’ Block the entire UI unnecessarily

Related Components