Marble UI
← Components

Focus Scope / Focus Trap

Preview: Contain

Import

import { FocusScope } from '@marbleui/ui'

Props

<FocusScope>

Re-exported from React Aria FocusScope; see its documentation for props.

Usage notes

Controls focus within a subtree: trap it, restore it, or move it programmatically. Dialogs, modals and popovers in Marble UI already do this, so you rarely need it directly.

<FocusScope contain restoreFocus autoFocus>
  <TextField label="Name" />
  <Button onPress={close}>Done</Button>
</FocusScope>
  • Re-exported from React Aria: contain traps Tab, restoreFocus returns focus on unmount, autoFocus focuses the first element on mount.
  • useFocusManager() inside a scope returns focusNext and focusPrevious for custom arrow-key navigation.
  • Always give users a way out of a contained scope.
Example source · show
import { useState, type KeyboardEvent } from 'react'
import { Button } from '../button'
import { TextField } from '../text-field'
import { FocusScope, useFocusManager } from './focus-scope'

function ArrowNav({ children }: { children: React.ReactNode }) {
  const fm = useFocusManager()
  const onKeyDown = (e: KeyboardEvent) => {
    if (e.key === 'ArrowRight') fm?.focusNext({ wrap: true })
    if (e.key === 'ArrowLeft') fm?.focusPrevious({ wrap: true })
  }
  return <div role="toolbar" aria-label="Arrow keys move focus" onKeyDown={onKeyDown} className="flex gap-2">{children}</div>
}

function Trap() {
  const [on, setOn] = useState(false)
  return (
    <div className="flex flex-col items-center gap-3">
      <Button variant="outline" onPress={() => setOn(true)}>Trap focus in the box</Button>
      {on && (
        <FocusScope contain restoreFocus autoFocus>
          <div className="flex w-72 flex-col gap-3 rounded-lg border border-accent p-4">
            <TextField label="Tab cycles inside" />
            <Button onPress={() => setOn(false)}>Release</Button>
          </div>
        </FocusScope>
      )}
    </div>
  )
}

export const styles = {
  'Contain': Trap,
  'Focus manager': () => (
    <FocusScope>
      <ArrowNav>
        <Button variant="outline">One</Button>
        <Button variant="outline">Two</Button>
        <Button variant="outline">Three</Button>
      </ArrowNav>
    </FocusScope>
  ),
}

Source: packages/ui/src/focus-scope/focus-scope.tsx

Catalog

Category
utilities
Priority
Core
Primitive
react-aria
React Aria
FocusScope