Appearance
useFocusTrap
useFocusTrap keeps keyboard focus inside the floating element while it is open.
Type
ts
function useFocusTrap(
context: UseFocusTrapContext,
options?: UseFocusTrapOptions,
): UseFocusTrapReturn;
interface UseFocusTrapContext {
state: FloatingContext["state"];
refs: FloatingContext["refs"];
}
interface UseFocusTrapOptions {
enabled?: MaybeRefOrGetter<boolean>;
modal?: MaybeRefOrGetter<boolean>;
initialFocus?: HTMLElement | (() => HTMLElement | false) | string | false;
returnFocus?: MaybeRefOrGetter<boolean>;
closeOnFocusOut?: MaybeRefOrGetter<boolean>;
preventScroll?: MaybeRefOrGetter<boolean>;
outsideElementsInert?: MaybeRefOrGetter<boolean>;
onError?: (error: unknown) => void;
ignoreFocusOut?: (target: EventTarget | null) => boolean;
}
interface UseFocusTrapReturn {
isActive: ComputedRef<boolean>;
activate: () => void;
deactivate: () => void;
}Details
useFocusTrap is the right interaction layer for dialogs and other modal surfaces. It can make the floating content modal, move initial focus, return focus on close, and optionally deactivate on outside click when you do not want a modal trap.
modalcontrols whether the trap behaves like a modal dialog.- Despite the option name,
closeOnFocusOutcurrently deactivates the trap on outside click whenmodalisfalse. initialFocuscan be an element, selector, function, orfalse.returnFocusdefaults totrue.preventScrolldefaults totrue.ignoreFocusOutis a predicate to determine if focus moving to a specific outside target should be ignored, preventing the focus trap from deactivating.onErrorreceives activation errors when the trap cannot be created.
Example
vue
<script setup lang="ts">
import { ref } from "vue";
import { useFocusTrap, useFloatingContext } from "v-float";
const anchorEl = ref<HTMLElement | null>(null);
const floatingEl = ref<HTMLElement | null>(null);
const open = ref(false);
const context = useFloatingContext({ refs: { anchorEl, floatingEl }, state: { open } });
useFocusTrap(context, { modal: true });
</script>
<template>
<button ref="anchorEl" @click="context.state.setOpen(!context.state.open.value)">
Open dialog
</button>
<Teleport to="body">
<div v-if="context.state.open.value" class="backdrop">
<div ref="floatingEl" role="dialog" aria-modal="true" tabindex="-1">
<h2>Dialog</h2>
<input placeholder="Focus stays inside" />
<button @click="context.state.setOpen(false)">Close</button>
</div>
</div>
</Teleport>
</template>