@rightxt/focus-trap-vue
v0.9.0
Published
This library provides a Vue-component wrapper around 'focus-trap'.
Readme
@rightxt/focus-trap-vue
This library provides a Vue-component wrapper around 'focus-trap'.
Before getting started, it is recommended to read the documentation to understand what a 'focus-trap' is.
Requirements
- Vue: ^3.5
- focus-trap: ^7.6.5
- Node: >=18
The library never bundles 'vue' and 'focus-trap'.
Installation
# npm
npm i @rightxt/focus-trap-vue
# pnpm
pnpm add @rightxt/focus-trap-vue
# yarn
yarn add @rightxt/focus-trap-vueBuild variants
The package publishes several artifacts.
| Variant | Import/Include | Suitable for | You must provide |
|--------------|------------------------------------------------------------------------|--------------------------|------------------------------------------------------|
| Core ESM | import plugin from '@rightxt/focus-trap-vue' | Vite / Webpack / Rollup | Install 'focus-trap' (peer) |
| IIFE | <script src="rxt-focus-trap-vue.iife.js"> → window.RxtFocusTrapVue | Static pages w/o bundler | Load before: UMD 'tabbable', UMD 'focus-trap', 'vue' |
As a plugin:
import { createApp } from 'vue';
import Plugin from '@rightxt/focus-trap-vue';
import App from './App.vue';
const app = createApp(App);
app.use(Plugin);
app.mount('#app');Component registration:
import { createApp } from 'vue';
import { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';
import App from './App.vue';
const app = createApp(App);
app.component('FocusTrap', FocusTrap);
app.component('FocusTrapExclude', FocusTrapExclude);
app.mount('#app');API
Package exports
default:
- plugin
{ install(app) }
Named:
FocusTrap— the main trap componentFocusTrapExclude— excludes a node/subtree from inertness- TypeScript types
import type {
ActivateOptions,
CreateOptions,
CreateOptionsProps,
DeactivateOptions,
ExclusionsApi,
FocusableElement,
FocusTarget,
FocusTargetOrFalse,
FocusTargetValueOrFalse,
FocusTrapI,
FocusTrapState,
FocusTrapTabbableOptions,
InertEntry,
KeyboardEventToBoolean,
MouseEventToBoolean,
PauseOptions,
PropsOptions,
RevertRecord,
UnpauseOptions,
VoidFunction,
FocusTrapVue,
} from '@rightxt/focus-trap-vue';
import Plugin, { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';Library components
FocusTrap
The component's content will be wrapped with 'focus-trap'.
| Prop | Type | Default | Description |
|------------------|-----------------|---------|----------------------------------------------------------------------------------------------------------------------------------------|
| v-model:active | boolean | false | Activates/deactivates the trap |
| v-model:paused | boolean | false | Pauses/unpauses the trap |
| createOptions | CreateOptions | null | Options proxied to 'focus-trap' (focus-trap) |
| renderless | boolean | false | If true, does not create a wrapper and wraps exactly one child |
| tag | string | 'div' | Root element tag when renderless = false |
Slots:
default— content within which focus is trapped
Events:
update:active(boolean)— trap activation state changeupdate:paused(boolean)— trap pause state changecreated— component createddestroyed— component destroyederror(where, error?)— an exception occurred inside the componentinner(handlerName)— events re-emitting internal 'focus-trap' events
Expose methods:
activate(options?)— activate the trap (focus-trap)deactivate(options?)— deactivate the trap (focus-trap)pause(options?)— pause an active trap (focus-trap)unpause(options?)— unpause an active trap (focus-trap)update()— force the component's DOM to update and reinitialize the trapgetState()— get the internal 'focus-trap' state as an object with two properties:{ active, paused }rootElement— a ref to the trap's root DOM element
Example:
<script setup lang="ts">
import { ref } from 'vue';
import { FocusTrap } from '@rightxt/focus-trap-vue';
const active = ref<boolean>(false);
const paused = ref<boolean>(false);
</script>
<template>
<FocusTrap v-model:active="active" v-model:paused="paused">...</FocusTrap>
</template>FocusTrapExclude
Marks a node/subtree inside the trap that should not become inert while the trap is active.
This component is only used inside FocusTrap!
Example:
<script setup lang="ts">
import { ref } from 'vue';
import { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';
const active = ref<boolean>(false);
const paused = ref<boolean>(false);
</script>
<template>
<FocusTrap v-model:active="active" v-model:paused="paused">
...
<FocusTrapExclude>
<p>Here is <a href="#">something</a> (excluded group).</p>
<p>Here is <a href="#">something</a> (excluded group).</p>
</FocusTrapExclude>
...
</FocusTrap>
</template>Demo
FAQ
Can I use it without the plugin?
Yes. Import it as a component.
Why does
v-model:pausedsometimes revert totrue?
If unpause() didn't take effect (the trap is not at the top of the stack), the component resyncs the model back so it reflects the trap's actual state. This matches 'focus-trap' behavior.
Why isn't the library a lightweight wrapper around 'focus-trap' like some alternatives, and what are its key features?
Because the library aims to ease developer experience by providing extra functionality for convenience.
The FocusTrap component creates an additional root HTML element by default. This ensures the trap works correctly even when there are temporarily no focusable elements inside — you don't have to track this manually.
If you set renderless=true, the component does not create a wrapper and wraps exactly one child. In this mode, the responsibility for having a proper container and focusable elements is on the developer.
You can control the trap declaratively (v-model:active, v-model:paused) or imperatively via expose methods (activate, deactivate, pause, unpause).
The additional FocusTrapExclude component lets you exclude specific elements or subtrees from the 'inertness' mode when the trap is active.
