Fiber UI LogoFiberUI

useFocus

A React hook that tracks whether an element is currently focused.

Installation

npx shadcn@latest add https://r.fiberui.com/r/hooks/use-focus.json

Features

  • Simple API - Just pass a ref and get a boolean.
  • SSR Safe - Works seamlessly with server-side rendering.
  • Accessibility - Great for building accessible interactive components.

Source Code

View the full hook implementation in the Hook Source Code section below.

Interactive Example

Focus Detection

Focus the input below to see the state change.

Status: Not Focused
"use client";

import { useRef } from "react";
import { useFocus } from "@repo/hooks/dom/use-focus";
import { Focus } from "lucide-react";

export const Example1 = () => {
    const ref = useRef<HTMLInputElement>(null);
    const isFocused = useFocus(ref);

    return (
        <div className="flex w-full max-w-md flex-col gap-4">
            <div className="flex items-center gap-2">
                <Focus className="text-primary h-5 w-5" />
                <h3 className="font-semibold">Focus Detection</h3>
            </div>

            <p className="text-muted-foreground text-sm">
                Focus the input below to see the state change.
            </p>

            <div className="relative">
                <input
                    ref={ref}
                    type="text"
                    placeholder="Click to focus..."
                    className={`border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border bg-transparent px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${
                        isFocused ? "border-primary" : ""
                    }`}
                />

                <div
                    className={`mt-2 text-sm transition-colors ${
                        isFocused
                            ? "text-primary font-medium"
                            : "text-muted-foreground"
                    }`}
                >
                    Status: {isFocused ? "Focused" : "Not Focused"}
                </div>
            </div>
        </div>
    );
};
const ref = useRef(null);
const isFocused = useFocus(ref);

Search Bar Focus Effect

Styles a parent container when the input is focused.

Search Bar Focus Effect

⌘K

The entire parent container styles change when the input inside receives focus.

"use client";

import { useRef } from "react";
import { useFocus } from "@repo/hooks/dom/use-focus";
import { Search } from "lucide-react";

export const Example2 = () => {
    const inputRef = useRef<HTMLInputElement>(null);
    const containerRef = useRef<HTMLDivElement>(null);
    // We can track focus on the input specifically
    const isInputFocused = useFocus(inputRef);

    // In a real app, you might want to track the container too
    // const isContainerFocused = useFocus(containerRef);

    return (
        <div className="flex w-full max-w-md flex-col gap-4">
            <h3 className="font-semibold">Search Bar Focus Effect</h3>

            <div
                ref={containerRef}
                className={`flex items-center gap-2 rounded-full border px-4 py-2 transition-all duration-300 ${
                    isInputFocused
                        ? "border-primary ring-primary/10 scale-105 shadow-lg ring-4"
                        : "border-muted bg-muted/30"
                }`}
            >
                <Search
                    className={`h-5 w-5 ${isInputFocused ? "text-primary" : "text-muted-foreground"}`}
                />
                <input
                    ref={inputRef}
                    type="text"
                    placeholder="Search documentation..."
                    className="placeholder:text-muted-foreground flex-1 bg-transparent text-sm outline-none"
                />
                <div className="text-muted-foreground bg-background/50 rounded border px-1.5 py-0.5 text-xs">
                    ⌘K
                </div>
            </div>

            <p className="text-muted-foreground text-center text-xs">
                The entire parent container styles change when the input inside
                receives focus.
            </p>
        </div>
    );
};
const isInputFocused = useFocus(inputRef); 

API Reference

Signature

const isFocused = useFocus(ref);

Parameters

ParameterTypeDescription
refRefObject<HTMLElement>React ref to the target element.

Returns

TypeDescription
booleantrue if the element is focused, false otherwise.

Hook Source Code

import { RefObject, useState, useCallback, useRef, useEffect } from "react";
import {
    useEventListener,
    EventListenerOptions,
} from "@repo/hooks/dom/use-event-listener";

/**
 * useFocus - Track focus state on an element
 *
 * @param ref - React ref pointing to the target element
 * @param callback - Optional callback when focus state changes
 * @param options - Optional event listener options
 * @returns boolean indicating if the element is currently focused
 *
 * @example
 * const ref = useRef<HTMLInputElement>(null);
 * const isFocused = useFocus(ref);
 *
 * // With callback
 * const isFocused = useFocus(ref, (focused) => console.log(focused));
 */
export function useFocus<T extends HTMLElement>(
    ref: RefObject<T | null>,
    callback?: (isFocused: boolean, event: FocusEvent) => void,
    options?: EventListenerOptions,
): boolean {
    const [isFocused, setIsFocused] = useState(false);
    const callbackRef = useRef(callback);

    useEffect(() => {
        callbackRef.current = callback;
    }, [callback]);

    const handleFocus = useCallback((event: FocusEvent) => {
        setIsFocused(true);
        callbackRef.current?.(true, event);
    }, []);

    const handleBlur = useCallback((event: FocusEvent) => {
        setIsFocused(false);
        callbackRef.current?.(false, event);
    }, []);

    useEventListener("focus", handleFocus, ref, options);
    useEventListener("blur", handleBlur, ref, options);

    return isFocused;
}