useFocusTrap
Traps focus within a specified element.
Examples
In these examples, clicking outside of an active focus trap will disable the focus trap. This is not the default behavior. It's just a workaround so focus behavior is not broken on the page if a user forgets to disable the focus trap after interacting with the example.
Default
Focus trap is disabled. Focus will not be trapped in here.
import React from 'react' import {useFocusTrap, useOnOutsideClick} from '@primer/react' export default function Default() { const [trapEnabled, setTrapEnabled] = React.useState(false) const {containerRef} = useFocusTrap({disabled: !trapEnabled}) /* START: not needed for using `useFocusTrap` This is just a workaround so focus behavior is not broken on the page if a user forgets to disable the focus trap after interacting with the example. */ const exampleContainerRef = React.useRef<HTMLDivElement>(null) useOnOutsideClick({ onClickOutside: () => { setTrapEnabled(false) }, containerRef: exampleContainerRef, }) /* END: not needed for using `useFocusTrap` */ return ( <div ref={exampleContainerRef} style={{display: 'flex', flexDirection: 'column', gap: '1rem'}}> <button onClick={() => setTrapEnabled(!trapEnabled)}>{trapEnabled ? 'Disable' : 'Enable'} trap</button> <button>Before trap</button> <div ref={containerRef as React.RefObject<HTMLDivElement>} style={{ border: '1px solid', margin: '1rem', padding: '1rem', display: 'flex', flexDirection: 'column', gap: '1rem', }} > {trapEnabled ? ( <div> Focus trap is <strong>enabled</strong>. Focus will be trapped in here. </div> ) : ( <div> Focus trap is <strong>disabled</strong>. Focus will <strong>not</strong> be trapped in here. </div> )} <button>First trapped</button> <button>Second trapped</button> <button>Third trapped</button> </div> <button>After trap</button> </div> ) }
Custom first focused element
Focus trap is disabled. Focus will not be trapped in here.
import React from 'react' import {useFocusTrap, useOnOutsideClick} from '@primer/react' export default function Default() { const [trapEnabled, setTrapEnabled] = React.useState(false) const initialFocusRef = React.useRef<HTMLButtonElement>(null) const {containerRef} = useFocusTrap({disabled: !trapEnabled, initialFocusRef}) /* START: not needed for using `useFocusTrap` This is just a workaround so focus behavior is not broken on the page if a user forgets to disable the focus trap after interacting with the example. */ const exampleContainerRef = React.useRef<HTMLDivElement>(null) useOnOutsideClick({ onClickOutside: () => { setTrapEnabled(false) }, containerRef: exampleContainerRef, }) /* END: not needed for using `useFocusTrap` */ return ( <div ref={exampleContainerRef} style={{display: 'flex', flexDirection: 'column', gap: '1rem'}}> <button onClick={() => setTrapEnabled(!trapEnabled)}>{trapEnabled ? 'Disable' : 'Enable'} trap</button> <button>Before trap</button> <div ref={containerRef as React.RefObject<HTMLDivElement>} style={{ border: '1px solid', margin: '1rem', padding: '1rem', display: 'flex', flexDirection: 'column', gap: '1rem', }} > {trapEnabled ? ( <div> Focus trap is <strong>enabled</strong>. Focus will be trapped in here. </div> ) : ( <div> Focus trap is <strong>disabled</strong>. Focus will <strong>not</strong> be trapped in here. </div> )} <button>First trapped</button> <button ref={initialFocusRef}>Second trapped (focus goes here first)</button> <button>Third trapped</button> </div> <button>After trap</button> </div> ) }
Return focus to the previously focused element after focus trap is turned off
Press the Escape key while focused in the trap to toggle the focus trap off.
When the focus trap is turned off, focus will return to the element focused before entering the focus trap.
Focus trap is disabled. Focus will not be trapped in here.
import React from 'react' import {useFocusTrap, useOnOutsideClick} from '@primer/react' export default function Toggle() { const [trapEnabled, setTrapEnabled] = React.useState(false) const {containerRef} = useFocusTrap({disabled: !trapEnabled, restoreFocusOnCleanUp: true}) /* Pressing the "Escape" key will turn the focus trap off */ React.useEffect(() => { if (!exampleContainerRef.current?.contains(document.activeElement)) { return } const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { setTrapEnabled(false) } } window.addEventListener('keydown', handleKeyDown) return () => { window.removeEventListener('keydown', handleKeyDown) } }) /* START: not needed for using `useFocusTrap` This is just a workaround so focus behavior is not broken on the page if a user forgets to disable the focus trap after interacting with the example. */ const exampleContainerRef = React.useRef<HTMLDivElement>(null) useOnOutsideClick({ onClickOutside: () => { setTrapEnabled(false) }, containerRef: exampleContainerRef, }) /* END: not needed for using `useFocusTrap` */ return ( <div ref={exampleContainerRef} style={{display: 'flex', flexDirection: 'column', gap: '1rem'}}> <button onClick={() => setTrapEnabled(!trapEnabled)}>{trapEnabled ? 'Disable' : 'Enable'} trap</button> <button>Before trap</button> <div ref={containerRef as React.RefObject<HTMLDivElement>} style={{ border: '1px solid', margin: '1rem', padding: '1rem', display: 'flex', flexDirection: 'column', gap: '1rem', }} > {trapEnabled ? ( <div> Focus trap is <strong>enabled</strong>. Focus will be trapped in here. </div> ) : ( <div> Focus trap is <strong>disabled</strong>. Focus will <strong>not</strong> be trapped in here. </div> )} <button>First trapped</button> <button>Second trapped</button> <button>Third trapped</button> </div> <button>After trap</button> </div> ) }
Return focus to a specific element after focus trap is turned off
Press the Escape key while focused in the trap to toggle the focus trap off.
When the focus trap is turned off, focus will always return to the same element.
Focus trap is disabled. Focus will not be trapped in here.
import React from 'react' import {useFocusTrap, useOnOutsideClick} from '@primer/react' export default function Toggle() { const [trapEnabled, setTrapEnabled] = React.useState(false) const returnFocusRef = React.useRef<HTMLButtonElement>(null) const {containerRef} = useFocusTrap({ disabled: !trapEnabled, // This ternary protects the element with `returnFocusRef` from being focused // as soon as the docs page is loaded. returnFocusRef: trapEnabled ? returnFocusRef : undefined, }) /* Pressing the "Escape" key will turn the focus trap off */ React.useEffect(() => { if (!exampleContainerRef.current?.contains(document.activeElement)) { return } const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { setTrapEnabled(false) } } window.addEventListener('keydown', handleKeyDown) return () => { window.removeEventListener('keydown', handleKeyDown) } }) /* START: not needed for using `useFocusTrap` This is just a workaround so focus behavior is not broken on the page if a user forgets to disable the focus trap after interacting with the example. */ const exampleContainerRef = React.useRef<HTMLDivElement>(null) useOnOutsideClick({ onClickOutside: () => { setTrapEnabled(false) }, containerRef: exampleContainerRef, }) /* END: not needed for using `useFocusTrap` */ return ( <div ref={exampleContainerRef} style={{display: 'flex', flexDirection: 'column', gap: '1rem'}}> <button onClick={() => setTrapEnabled(!trapEnabled)}>{trapEnabled ? 'Disable' : 'Enable'} trap</button> <button ref={returnFocusRef}>Disabling the trap moves focus here</button> <button>Before trap</button> <div ref={containerRef as React.RefObject<HTMLDivElement>} style={{ border: '1px solid', margin: '1rem', padding: '1rem', display: 'flex', flexDirection: 'column', gap: '1rem', }} > {trapEnabled ? ( <div> Focus trap is <strong>enabled</strong>. Focus will be trapped in here. </div> ) : ( <div> Focus trap is <strong>disabled</strong>. Focus will <strong>not</strong> be trapped in here. </div> )} <button>First trapped</button> <button>Second trapped</button> <button>Third trapped</button> </div> <button>After trap</button> </div> ) }
API
Loading data for useFocusTrap...