Hooks
useFocusTrap
Trap keyboard focus within a container while active. Tab and Shift+Tab cycle through the focusable descendants only. Focuses an initial element on activation, and by default restores focus to the previously focused element on deactivation or unmount. SSR-safe.
Note: Components built on bits-ui primitives (Modal, Slideover, Drawer, Popover) already manage focus internally - do not wrap them with
useFocusTrap.Basic usage
Bind the container with bind:this, pass a reactive getter for active. When open is true, Tab cycles only among the elements inside the panel.
<script lang="ts">
import { useFocusTrap } from 'sv5ui';
let open = $state(false);
let panel = $state<HTMLDivElement>();
useFocusTrap(() => panel, { active: () => open });
</script>
<button onclick={() => (open = true)}>Open panel</button>
{#if open}
<div bind:this={panel} class="rounded border p-4">
<p>Focus is trapped here. Tab cycles through the buttons below.</p>
<input placeholder="First focusable" class="block rounded border px-2 py-1" />
<input placeholder="Second focusable" class="block rounded border px-2 py-1 mt-2" />
<button onclick={() => (open = false)} class="mt-2">Close</button>
</div>
{/if}Custom initial focus
Use initialFocus to point focus at a specific element (e.g. a close button) instead of the first focusable element. The getter is resolved at activation time.
<script lang="ts">
import { useFocusTrap } from 'sv5ui';
let open = $state(false);
let panel = $state<HTMLDivElement>();
let closeBtn = $state<HTMLButtonElement>();
useFocusTrap(() => panel, {
active: () => open,
// Move focus to the close button on open instead of the first focusable
initialFocus: () => closeBtn ?? null,
// Return focus to the trigger on close (default: true)
restoreFocus: true
});
</script>
<button onclick={() => (open = true)}>Open panel</button>
{#if open}
<div bind:this={panel} class="rounded border p-4">
<input placeholder="Not focused first" class="block rounded border px-2 py-1" />
<button bind:this={closeBtn} onclick={() => (open = false)} class="mt-2">
Close (gets initial focus)
</button>
</div>
{/if}Parameters
| Parameter | Type | Default |
|---|---|---|
target | HTMLElement | (() => HTMLElement | null) | null | - |
Options
| Option | Type | Default |
|---|---|---|
active | boolean | (() => boolean) | true |
initialFocus | HTMLElement | (() => HTMLElement | null) | false | first focusable |
restoreFocus | boolean | true |