# Focus Trap

URL: https://ark-ui.com/docs/utilities/focus-trap
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/utilities/focus-trap.mdx

Trap focus within a specified container.

---

## Motivation

Focus trapping is essential for modal interfaces and other interactive elements that require user attention.

The `FocusTrap` component helps maintain accessibility by ensuring keyboard focus remains within a designated container
until explicitly released.

## Examples

**Example: basic**

#### React

```tsx
import { FocusTrap } from '@ark-ui/react/focus-trap'
import { useState } from 'react'

export const Basic = () => {
  const [trapped, setTrapped] = useState(false)
  return (
    <>
      <button onClick={() => setTrapped(true)}>Start Trap</button>
      <FocusTrap returnFocusOnDeactivate={false} disabled={!trapped}>
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            gap: '1rem',
            paddingBlock: '1rem',
          }}
        >
          <input type="text" placeholder="input" />
          <textarea placeholder="textarea" />
          <button onClick={() => setTrapped(false)}>End Trap</button>
        </div>
      </FocusTrap>
    </>
  )
}

```

#### Solid

```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { createSignal } from 'solid-js'

export const Basic = () => {
  const [trapped, setTrapped] = createSignal(false)
  return (
    <>
      <button onClick={() => setTrapped(true)}>Start Trap</button>
      <FocusTrap returnFocusOnDeactivate={false} disabled={!trapped()}>
        <div
          style={{
            display: 'flex',
            'flex-direction': 'column',
            gap: '1rem',
            'padding-block': '1rem',
          }}
        >
          <input type="text" placeholder="input" />
          <textarea placeholder="textarea" />
          <button onClick={() => setTrapped(false)}>End Trap</button>
        </div>
      </FocusTrap>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { FocusTrap } from '@ark-ui/vue/focus-trap'
import { ref } from 'vue'

const trapped = ref(false)
</script>

<template>
  <button @click="trapped = true">Start Trap</button>
  <FocusTrap :return-focus-on-deactivate="false" :disabled="!trapped">
    <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
      <input type="text" placeholder="input" />
      <textarea placeholder="textarea" />
      <button @click="trapped = false">End Trap</button>
    </div>
  </FocusTrap>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { FocusTrap } from '@ark-ui/svelte/focus-trap'

  let trapped = $state(false)
</script>

<button onclick={() => (trapped = true)}>Start Trap</button>

<FocusTrap returnFocusOnDeactivate={false} disabled={!trapped}>
  <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
    <input type="text" placeholder="input" />
    <textarea placeholder="textarea"></textarea>
    <button onclick={() => (trapped = false)}>End Trap</button>
  </div>
</FocusTrap>

```

### Autofocus

The focus trap respects elements with the `autofocus` attribute.

**Example: autofocus**

#### React

```tsx
import { FocusTrap } from '@ark-ui/react/focus-trap'
import { useRef, useState } from 'react'

export const Autofocus = () => {
  const [trapped, setTrapped] = useState(false)
  const toggle = () => setTrapped((c) => !c)

  const buttonRef = useRef<HTMLButtonElement | null>(null)
  const getButtonNode = () => {
    const node = buttonRef.current
    if (!node) throw new Error('Button not found')
    return node
  }

  return (
    <div>
      <button ref={buttonRef} onClick={toggle}>
        {trapped ? 'End Trap' : 'Start Trap'}
      </button>
      {trapped && (
        <FocusTrap disabled={!trapped} setReturnFocus={getButtonNode}>
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
              gap: '1rem',
              paddingBlock: '1rem',
            }}
          >
            <input type="text" placeholder="Regular input" />
            {/* biome-ignore lint/a11y/noAutofocus: intentional */}
            <input type="text" placeholder="Autofocused input" autoFocus />
            <button onClick={() => setTrapped(false)}>End Trap</button>
          </div>
        </FocusTrap>
      )}
    </div>
  )
}

```

#### Solid

```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { Show, createSignal } from 'solid-js'

export const Autofocus = () => {
  const [trapped, setTrapped] = createSignal(false)
  let buttonRef!: HTMLButtonElement

  return (
    <div>
      <button ref={buttonRef} onClick={() => setTrapped((v) => !v)}>
        {trapped() ? 'End Trap' : 'Start Trap'}
      </button>
      <Show when={trapped()}>
        <FocusTrap disabled={!trapped()} setReturnFocus={buttonRef}>
          <div
            style={{
              display: 'flex',
              'flex-direction': 'column',
              gap: '1rem',
              'padding-block': '1rem',
            }}
          >
            <input type="text" placeholder="Regular input" />
            <input type="text" placeholder="Autofocused input" autofocus />
            <button onClick={() => setTrapped(false)}>End Trap</button>
          </div>
        </FocusTrap>
      </Show>
    </div>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { FocusTrap } from '@ark-ui/vue/focus-trap'
import { ref } from 'vue'

const trapped = ref(false)
const buttonRef = ref<HTMLButtonElement>()
</script>

<template>
  <div>
    <button ref="buttonRef" @click="trapped = !trapped">
      {{ trapped ? 'End Trap' : 'Start Trap' }}
    </button>
    <FocusTrap v-if="trapped" :disabled="!trapped" :set-return-focus="buttonRef">
      <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
        <input type="text" placeholder="Regular input" />
        <input type="text" placeholder="Autofocused input" autofocus />
        <button @click="trapped = false">End Trap</button>
      </div>
    </FocusTrap>
  </div>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { FocusTrap } from '@ark-ui/svelte/focus-trap'

  let trapped = $state(false)
  let buttonNode: HTMLButtonElement | null = $state(null)

  function getButtonNode() {
    if (!buttonNode) throw new Error('Button not found')
    return buttonNode
  }
</script>

<div>
  <button bind:this={buttonNode} onclick={() => (trapped = !trapped)}>
    {trapped ? 'End Trap' : 'Start Trap'}
  </button>
  {#if trapped}
    <FocusTrap disabled={!trapped} setReturnFocus={getButtonNode}>
      <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
        <input type="text" placeholder="Regular input" />
        <!-- svelte-ignore a11y_autofocus -->
        <input type="text" placeholder="Autofocused input" autofocus />
        <button onclick={() => (trapped = false)}>End Trap</button>
      </div>
    </FocusTrap>
  {/if}
</div>

```

### Initial Focus

Use the `initialFocus` prop to set the element that should receive initial focus when the trap is activated.

**Example: initial-focus**

#### React

```tsx
import { FocusTrap } from '@ark-ui/react/focus-trap'
import { useRef, useState } from 'react'

export const InitialFocus = () => {
  const [trapped, setTrapped] = useState(false)
  const toggle = () => setTrapped((c) => !c)

  const inputRef = useRef<HTMLInputElement>(null)

  return (
    <div>
      <button onClick={toggle}>{trapped ? 'End Trap' : 'Start Trap'}</button>
      <FocusTrap disabled={!trapped} initialFocus={() => inputRef.current}>
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            gap: '1rem',
            paddingBlock: '1rem',
          }}
        >
          <input type="text" placeholder="First input" />
          <input ref={inputRef} type="text" placeholder="Second input (initial focus)" />
          <textarea placeholder="textarea" />
          <button onClick={() => setTrapped(false)}>End Trap</button>
        </div>
      </FocusTrap>
    </div>
  )
}

```

#### Solid

```tsx
import { FocusTrap } from '@ark-ui/solid/focus-trap'
import { createSignal } from 'solid-js'

export const InitialFocus = () => {
  const [trapped, setTrapped] = createSignal(false)
  const toggle = () => setTrapped((v) => !v)

  let inputRef!: HTMLInputElement

  return (
    <div>
      <button onClick={toggle}>{trapped() ? 'End Trap' : 'Start Trap'}</button>
      <FocusTrap disabled={!trapped()} initialFocus={() => inputRef}>
        <div
          style={{
            display: 'flex',
            'flex-direction': 'column',
            gap: '1rem',
            'padding-block': '1rem',
          }}
        >
          <input type="text" placeholder="First input" />
          <input ref={inputRef} type="text" placeholder="Second input (initial focus)" />
          <textarea placeholder="textarea" />
          <button onClick={toggle}>End Trap</button>
        </div>
      </FocusTrap>
    </div>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { FocusTrap } from '@ark-ui/vue/focus-trap'
import { ref } from 'vue'

const trapped = ref(false)
const inputRef = ref<HTMLInputElement>()
const toggle = () => {
  trapped.value = !trapped.value
}
</script>

<template>
  <div>
    <button @click="toggle">{{ trapped ? 'End Trap' : 'Start Trap' }}</button>
    <FocusTrap :disabled="!trapped" :initial-focus="() => inputRef">
      <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
        <input type="text" placeholder="First input" />
        <input ref="inputRef" type="text" placeholder="Second input (initial focus)" />
        <textarea placeholder="textarea" />
        <button @click="trapped = false">End Trap</button>
      </div>
    </FocusTrap>
  </div>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { FocusTrap } from '@ark-ui/svelte/focus-trap'

  let trapped = $state(false)
  let inputNode: HTMLInputElement | null = $state(null)

  function getInputNode() {
    if (!inputNode) throw new Error('Input not found')
    return inputNode
  }
</script>

<div>
  <button onclick={() => (trapped = !trapped)}>
    {trapped ? 'End Trap' : 'Start Trap'}
  </button>
  <FocusTrap disabled={!trapped} initialFocus={getInputNode}>
    <div style="display: flex; flex-direction: column; gap: 1rem; padding-block: 1rem">
      <input type="text" placeholder="First input" />
      <input bind:this={inputNode} type="text" placeholder="Second input (initial focus)" />
      <textarea placeholder="textarea"></textarea>
      <button onclick={() => (trapped = false)}>End Trap</button>
    </div>
  </FocusTrap>
</div>

```

## API Reference

<ComponentTypes id="focus-trap" />