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.jsonFeatures
- 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
| Parameter | Type | Description |
|---|---|---|
ref | RefObject<HTMLElement> | React ref to the target element. |
Returns
| Type | Description |
|---|---|
boolean | true 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;
}