Skip to content

useActiveElement()

Category
Export Size
784 B
Gzipped
322 B
SSR
Not support

Live demo: useActiveElement

Mirror document.activeElement and inspect focus transitions across inputs, textarea, buttons, and links.

Loading demo...

Demo sourceJSX
JSX

Overview

useActiveElement mirrors document.activeElement into React state by listening to focusin / focusout on document, so your UI can react whenever focus moves between inputs, buttons, links, or contenteditable regions (including focus changes caused by programmatic .focus() calls). The initial value reads document.activeElement on the client and falls back to null during SSR; after mount, updates happen on every focus transition until the component unmounts.

What it accepts

  • No arguments.

What it returns

  • The current document.activeElement, or null when unavailable. Type Element | null.

Usage

Real-world example: show which control is currently focused in a small form (no document.activeElement calls in render logic).

tsx
import useActiveElement from '@dedalik/use-react/useActiveElement'

function Example() {
  const active = useActiveElement()

  const describe = (el: Element | null) => {
    if (!el || !(el instanceof HTMLElement)) return 'none'
    const id = el.id ? `#${el.id}` : ''
    const name = 'name' in el && typeof (el as HTMLInputElement).name === 'string' ? (el as HTMLInputElement).name : ''
    return `${el.tagName.toLowerCase()}${id}${name ? `[name=${name}]` : ''}`
  }

  return (
    <div>
      <h3>Focus inspector</h3>
      <p>
        Active element: <code>{describe(active)}</code>
      </p>

      <div style={{ display: 'grid', gap: 8, maxWidth: 420 }}>
        <label htmlFor='email'>
          Email
          <input id='email' name='email' type='email' placeholder='you@example.com' style={{ width: '100%' }} />
        </label>
        <label htmlFor='notes'>
          Notes
          <textarea id='notes' name='notes' rows={3} placeholder='Type here…' style={{ width: '100%' }} />
        </label>
        <div style={{ display: 'flex', gap: 8 }}>
          <button type='button'>Save</button>
          <button type='button'>Cancel</button>
        </div>
      </div>
    </div>
  )
}

export default function Demo() {
  return <Example />
}

API Reference

useActiveElement

Signature: useActiveElement(): Element | null

Parameters

None.

Returns

The current document.activeElement, or null when unavailable. (Element | null).

Copy-paste hook

TypeScript

tsx
import { useEffect, useState } from 'react'

/**
 * Tracks current document.activeElement.
 */
export default function useActiveElement(): Element | null {
  const [activeElement, setActiveElement] = useState<Element | null>(() =>
    typeof document !== 'undefined' ? document.activeElement : null,
  )

  useEffect(() => {
    const update = () => setActiveElement(document.activeElement)

    document.addEventListener('focusin', update)
    document.addEventListener('focusout', update)

    return () => {
      document.removeEventListener('focusin', update)
      document.removeEventListener('focusout', update)
    }
  }, [])

  return activeElement
}

JavaScript

js
import { useEffect, useState } from 'react'

export default function useActiveElement() {
  const [activeElement, setActiveElement] = useState(() =>
    typeof document !== 'undefined' ? document.activeElement : null,
  )

  useEffect(() => {
    const update = () => setActiveElement(document.activeElement)

    document.addEventListener('focusin', update)
    document.addEventListener('focusout', update)

    return () => {
      document.removeEventListener('focusin', update)
      document.removeEventListener('focusout', update)
    }
  }, [])

  return activeElement
}

Released under the MIT License.