HextaUI

Popover: Content that changes size

When the content grows or shrinks, the popup animates its height instead of jumping. Continuous changes, like typing, follow the content directly so nothing lags behind.

"use client"

import * as React from "react"
import { IconBell } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
import { Skeleton } from "@/components/ui/skeleton"

export function PopoverResizing() {
  const [rows, setRows] = React.useState(1)
  const [loading, setLoading] = React.useState(false)
  const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined)

  React.useEffect(() => () => clearTimeout(timer.current), [])

  return (
    <Popover
      onOpenChange={(open) => {
        if (open) {
          setRows(1)
          setLoading(true)
          clearTimeout(timer.current)
          timer.current = setTimeout(() => setLoading(false), 700)
        }
      }}
    >
      <PopoverTrigger render={<Button variant="outline" />}>
        <IconBell />
        Notifications
      </PopoverTrigger>
      <PopoverContent>
        <PopoverHeader>
          <PopoverTitle>Notifications</PopoverTitle>
          <PopoverDescription>
            The height animates as content loads and grows.
          </PopoverDescription>
        </PopoverHeader>
        {loading ? (
          <Skeleton className="h-10 w-full" />
        ) : (
          <ul className="flex flex-col gap-2">
            {Array.from({ length: rows }, (_, index) => (
              <li
                key={index}
                className="rounded-md bg-muted px-2.5 py-2 text-sm"
              >
                Deploy #{1200 + index} finished in {12 + index}s
              </li>
            ))}
          </ul>
        )}
        <div className="flex gap-2">
          <Button
            variant="outline"
            size="sm"
            disabled={loading}
            onClick={() => setRows(Math.min(rows + 2, 12))}
          >
            Load more
          </Button>
          <Button
            variant="ghost"
            size="sm"
            disabled={loading || rows === 1}
            onClick={() => setRows(1)}
          >
            Collapse
          </Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

This adds the example as components/examples/popover/resizing.tsx, together with the Popover component and the HextaUI theme tokens it needs.

pnpm dlx shadcn@latest add https://hextaui.com/r/popover-resizing.json

To add only the component, follow the Popover installation.

Props in this example

The Popover props this example sets, from the API reference.

PropTypeDefault
onOpenChangedetails.reason says what caused the change.
(open: boolean, details) => void–
PropTypeDefault
render
ReactElement | (props, state) => ReactElement<button>

Every prop is in the Popover API reference.

See them all, with keyboard support and accessibility notes, on the Popover page.