Popover

Display supplementary content or information when users interact with specific elements.

llms.txt
	<script lang="ts">
  import { Popover, Separator, Toggle } from "bits-ui";
  import ImageSquare from "phosphor-svelte/lib/ImageSquare";
  import LinkSimpleHorizontalBreak from "phosphor-svelte/lib/LinkSimpleHorizontalBreak";
 
  let width = $state(1024);
  let height = $state(768);
</script>
 
<Popover.Root>
  <Popover.Trigger
    class="rounded-input bg-dark
	text-background shadow-mini hover:bg-dark/95 inline-flex h-10 select-none items-center justify-center whitespace-nowrap px-[21px] text-[15px] font-medium transition-all hover:cursor-pointer active:scale-[0.98]"
  >
    Resize
  </Popover.Trigger>
  <Popover.Portal>
    <Popover.Content
      class="border-dark-10 bg-background shadow-popover data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--bits-popover-content-transform-origin) z-30 w-full max-w-[328px] rounded-[12px] border p-4"
      sideOffset={8}
    >
      <div class="flex items-center">
        <div
          class="bg-muted mr-3 flex size-12 items-center justify-center rounded-full"
        >
          <ImageSquare class="size-6" />
        </div>
        <div class="flex flex-col">
          <h4 class="text-[17px] font-semibold leading-5 tracking-[-0.01em]">
            Resize image
          </h4>
          <p class="text-muted-foreground text-sm font-medium">
            Resize your photos easily
          </p>
        </div>
      </div>
      <Separator.Root class="bg-dark-10 -mx-4 mb-6 mt-[17px] block h-px" />
      <div class="flex items-center pb-2">
        <div class="mr-2 flex items-center">
          <div class="relative mr-2">
            <span class="sr-only">Width</span>
            <span
              aria-hidden="true"
              class="text-xxs text-muted-foreground absolute left-5 top-4"
              >W</span
            >
            <input
              type="number"
              class="h-input rounded-10px border-border-input bg-background text-foreground w-[119px] border pl-10 pr-2 text-base sm:text-sm"
              bind:value={width}
            />
          </div>
          <div class="relative">
            <span class="sr-only">Height</span>
            <span
              aria-hidden="true"
              class="text-xxs text-muted-foreground absolute left-5 top-4"
              >H</span
            >
            <input
              type="number"
              class="h-input rounded-10px border-border-input bg-background text-foreground w-[119px] border pl-10 pr-2 text-base sm:text-sm"
              bind:value={height}
            />
          </div>
        </div>
        <Toggle.Root
          aria-label="toggle constrain portions"
          class="bg-background hover:bg-muted data-[state=on]:bg-muted inline-flex size-10 items-center justify-center rounded-[9px] transition-all active:scale-[0.98]"
        >
          <LinkSimpleHorizontalBreak class="size-6" />
        </Toggle.Root>
      </div>
    </Popover.Content>
  </Popover.Portal>
</Popover.Root>

Structure

	<script lang="ts">
  import { Popover } from "bits-ui";
</script>
 
<Popover.Root>
  <Popover.Trigger />
  <Popover.Content>
    <Popover.Close />
    <Popover.Arrow />
  </Popover.Content>
</Popover.Root>

Managing Open State

This section covers how to manage the open state of the component.

Two-Way Binding

Use bind:open for simple, automatic state synchronization:

	<script lang="ts">
  import { Popover } from "bits-ui";
  let isOpen = $state(false);
</script>
 
<button onclick={() => (isOpen = true)}>Open Popover</button>
 
<Popover.Root bind:open={isOpen}>
  <!-- ... -->
</Popover.Root>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { Popover } from "bits-ui";
  let myOpen = $state(false);
 
  function getOpen() {
    return myOpen;
  }
 
  function setOpen(newOpen: boolean) {
    myOpen = newOpen;
  }
</script>
 
<Popover.Root bind:open={getOpen, setOpen}>
  <!-- ... -->
</Popover.Root>

Managing Focus

Focus Trap

By default, when a Popover is opened, focus will be trapped within that Popover. You can disable this behavior by setting the trapFocus prop to false on the Popover.Content component.

	<Popover.Content trapFocus={false}>
  <!-- ... -->
</Popover.Content>

Open Focus

By default, when a Popover is opened, focus will be set to the first focusable element with the Popover.Content. This ensures that users navigating my keyboard end up somewhere within the Popover that they can interact with.

You can override this behavior using the onOpenAutoFocus prop on the Popover.Content component. It's highly recommended that you use this prop to focus something within the Popover's content.

You'll first need to cancel the default behavior of focusing the first focusable element by cancelling the event passed to the onOpenAutoFocus callback. You can then focus whatever you wish.

	<script lang="ts">
  import { Popover } from "bits-ui";
  let nameInput = $state<HTMLInputElement>();
</script>
 
<Popover.Root>
  <Popover.Trigger>Open Popover</Popover.Trigger>
  <Popover.Content
    onOpenAutoFocus={(e) => {
      e.preventDefault();
      nameInput?.focus();
    }}
  >
    <input type="text" bind:this={nameInput} />
  </Popover.Content>
</Popover.Root>

Close Focus

By default, when a Popover is closed, focus will be set to the trigger element of the Popover. You can override this behavior using the onCloseAutoFocus prop on the Popover.Content component.

You'll need to cancel the default behavior of focusing the trigger element by cancelling the event passed to the onCloseAutoFocus callback, and then focus whatever you wish.

	<script lang="ts">
  import { Popover } from "bits-ui";
  let nameInput = $state<HTMLInputElement>();
</script>
 
<input type="text" bind:this={nameInput} />
<Popover.Root>
  <Popover.Trigger>Open Popover</Popover.Trigger>
  <Popover.Content
    onCloseAutoFocus={(e) => {
      e.preventDefault();
      nameInput?.focus();
    }}
  >
    <!-- ... -->
  </Popover.Content>
</Popover.Root>

Scroll Lock

By default, when a Popover is opened, users can still scroll the body and interact with content outside of the Popover. If you wish to lock the body scroll and prevent users from interacting with content outside of the Popover, you can set the preventScroll prop to true on the Popover.Content component.

	<Popover.Content preventScroll={true}>
  <!-- ... -->
</Popover.Content>

Escape Keydown

By default, when a Popover is open, pressing the Escape key will close the dialog. Bits UI provides a couple ways to override this behavior.

escapeKeydownBehavior

You can set the escapeKeydownBehavior prop to 'ignore' on the Popover.Content component to prevent the dialog from closing when the Escape key is pressed.

	<Popover.Content escapeKeydownBehavior="ignore">
  <!-- ... -->
</Popover.Content>

onEscapeKeydown

You can also override the default behavior by cancelling the event passed to the onEscapeKeydown callback on the Popover.Content component.

	<Popover.Content onEscapeKeydown={(e) => e.preventDefault()}>
  <!-- ... -->
</Popover.Content>

Interact Outside

By default, when a Popover is open, pointer down events outside the content will close the popover. Bits UI provides a couple ways to override this behavior.

interactOutsideBehavior

You can set the interactOutsideBehavior prop to 'ignore' on the Popover.Content component to prevent the dialog from closing when the user interacts outside the content.

	<Popover.Content interactOutsideBehavior="ignore">
  <!-- ... -->
</Popover.Content>

onInteractOutside

You can also override the default behavior by cancelling the event passed to the onInteractOutside callback on the Popover.Content component.

	<Popover.Content onInteractOutside={(e) => e.preventDefault()}>
  <!-- ... -->
</Popover.Content>

Custom Anchor

By default, the Popover.Content is anchored to the Popover.Trigger component, which determines where the content is positioned.

If you wish to instead anchor the content to a different element, you can pass either a selector string or an HTMLElement to the customAnchor prop of the Popover.Content component.

	<script lang="ts">
  import { Popover } from "bits-ui";
  let customAnchor = $state<HTMLElement>(null!);
</script>
 
<div bind:this={customAnchor}></div>
 
<Popover.Root>
  <Popover.Trigger />
  <Popover.Content {customAnchor}>
    <!-- ... -->
  </Popover.Content>
</Popover.Root>

Svelte Transitions

You can use the forceMount prop along with the child snippet to forcefully mount the Popover.Content component to use Svelte Transitions or another animation library that requires more control.

	<script lang="ts">
  import { Popover } from "bits-ui";
  import { fly } from "svelte/transition";
</script>
 
<Popover.Content forceMount>
  {#snippet child({ wrapperProps, props, open })}
    {#if open}
      <div {...wrapperProps}>
        <div {...props} transition:fly>
          <!-- ... -->
        </div>
      </div>
    {/if}
  {/snippet}
</Popover.Content>

Of course, this isn't the prettiest syntax, so it's recommended to create your own reusable content component that handles this logic if you intend to use this approach. For more information on using transitions with Bits UI components, see the Transitions documentation.

	<script lang="ts">
  import { Popover, Separator, Toggle } from "bits-ui";
  import ImageSquare from "phosphor-svelte/lib/ImageSquare";
  import LinkSimpleHorizontalBreak from "phosphor-svelte/lib/LinkSimpleHorizontalBreak";
  import { fly } from "svelte/transition";
 
  let width = $state(1024);
  let height = $state(768);
</script>
 
<Popover.Root>
  <Popover.Trigger
    class="rounded-input bg-dark
	text-background shadow-mini hover:bg-dark/95 inline-flex h-10 select-none items-center justify-center whitespace-nowrap px-[21px] text-[15px] font-medium transition-all hover:cursor-pointer active:scale-[0.98]"
  >
    Resize
  </Popover.Trigger>
  <Popover.Portal>
    <Popover.Content
      class="border-dark-10 bg-background shadow-popover z-30 w-full max-w-[328px] rounded-[12px] border p-4"
      sideOffset={8}
      forceMount
    >
      {#snippet child({ wrapperProps, props, open })}
        {#if open}
          <div {...wrapperProps}>
            <div {...props} transition:fly={{ duration: 300 }}>
              <div class="flex items-center">
                <div
                  class="bg-muted mr-3 flex size-12 items-center justify-center rounded-full"
                >
                  <ImageSquare class="size-6" />
                </div>
                <div class="flex flex-col">
                  <h4
                    class="text-[17px] font-semibold leading-5 tracking-[-0.01em]"
                  >
                    Resize image
                  </h4>
                  <p class="text-muted-foreground text-sm font-medium">
                    Resize your photos easily
                  </p>
                </div>
              </div>
              <Separator.Root
                class="bg-dark-10 -mx-4 mb-6 mt-[17px] block h-px"
              />
              <div class="flex items-center pb-2">
                <div class="mr-2 flex items-center">
                  <div class="relative mr-2">
                    <span class="sr-only">Width</span>
                    <span
                      aria-hidden="true"
                      class="text-xxs text-muted-foreground absolute left-5 top-4"
                      >W</span
                    >
                    <input
                      type="number"
                      class="h-input rounded-10px border-border-input bg-background text-foreground w-[119px] border pl-10 pr-2 text-base sm:text-sm"
                      bind:value={width}
                    />
                  </div>
                  <div class="relative">
                    <span class="sr-only">Height</span>
                    <span
                      aria-hidden="true"
                      class="text-xxs text-muted-foreground absolute left-5 top-4"
                      >H</span
                    >
                    <input
                      type="number"
                      class="h-input rounded-10px border-border-input bg-background text-foreground w-[119px] border pl-10 pr-2 text-base sm:text-sm"
                      bind:value={height}
                    />
                  </div>
                </div>
                <Toggle.Root
                  aria-label="toggle constrain portions"
                  class="bg-background hover:bg-muted data-[state=on]:bg-muted inline-flex size-10 items-center justify-center rounded-[9px] transition-all active:scale-[0.98]"
                >
                  <LinkSimpleHorizontalBreak class="size-6" />
                </Toggle.Root>
              </div>
            </div>
          </div>
        {/if}
      {/snippet}
    </Popover.Content>
  </Popover.Portal>
</Popover.Root>

API Reference

Popover.Root

The root component used to manage the state of the state of the popover.

Property Details
open
onOpenChange
onOpenChangeComplete
children
Data Attribute Details

Popover.Trigger

A component which toggles the opening and closing of the popover on press.

Property Details
ref
children
child
Data Attribute Details
data-state
data-popover-trigger

Popover.Content

The contents of the popover which are displayed when the popover is open.

Property Details
side
sideOffset
align
alignOffset
arrowPadding
avoidCollisions
collisionBoundary
collisionPadding
sticky
hideWhenDetached
updatePositionStrategy
strategy
preventScroll
customAnchor
onInteractOutside
onFocusOutside
interactOutsideBehavior
onEscapeKeydown
escapeKeydownBehavior
onOpenAutoFocus
onCloseAutoFocus
trapFocus
preventOverflowTextSelection
forceMount
dir
ref
children
child
Data Attribute Details
data-state
data-popover-content
CSS Variable Details
--bits-popover-content-transform-origin
--bits-popover-content-available-width
--bits-popover-content-available-height
--bits-popover-anchor-width
--bits-popover-anchor-height

Popover.ContentStatic

The contents of the popover which are displayed when the popover is open. (Static/No Floating UI)

Property Details
onInteractOutside
onFocusOutside
interactOutsideBehavior
onEscapeKeydown
escapeKeydownBehavior
onOpenAutoFocus
onCloseAutoFocus
trapFocus
preventOverflowTextSelection
preventScroll
forceMount
dir
ref
children
child
Data Attribute Details
data-state
data-popover-content

Popover.Close

A button which closes the popover when pressed and is typically placed in the content.

Property Details
ref
children
child
Data Attribute Details
data-popover-close

Popover.Arrow

An optional arrow element which points to the trigger when the popover is open.

Property Details
width
height
ref
children
child
Data Attribute Details
data-arrow
data-popover-arrow

Popover.Portal

When used, will render the popover content into the body or custom to element when open

Property Details
to
disabled
children
Data Attribute Details