elements
lifecycle
browser
- useAudio
- useBattery
- useBluetooth
- useBreakpoints
- useBroadcastChannel
- useBrowserLocation
- useClipboard
- useCopy
- useCssVar
- useDisplayMedia
- useDocumentEvent
- useDocumentTitle
- useDocumentVisibility
- useEventListener
- useEventSource
- useEyeDropper
- useFavicon
- useFileSystemAccess
- useFps
- useFullscreen
- useGamepad
- useGeolocation
- useMeasure
- useMediaControls
- useMediaQuery
- useMemory
- useNetwork
- useObjectUrl
- useOnline
- useOtpCredential
- usePermission
- usePictureInPicture
- usePointerLock
- usePostMessage
- useRaf
- useShare
- useSpeechRecognition
- useSpeechSynthesis
- useSticky
- useVibrate
- useVirtualKeyboard
- useWakeLock
- useWebSocket
utilities
state
- useBoolean
- useControllableState
- useCookie
- useCookies
- useCounter
- useCycleList
- useDefault
- useDisclosure
- useField
- useHash
- useList
- useLocalStorage
- useMap
- useMask
- useMergedRef
- useObject
- useOffsetPagination
- useQueue
- useRafState
- useRefState
- useSessionStorage
- useSet
- useStateHistory
- useStep
- useStorage
- useToggle
- useUrlSearchParam
- useUrlSearchParams
- useValidatedState
- useWizard
user
sensors
- useDeviceMotion
- useDeviceOrientation
- useHotkeys
- useIdle
- useInfiniteScroll
- useIntersectionObserver
- useKeyboard
- useKeyPress
- useKeysPressed
- useMouse
- useMutationObserver
- useOrientation
- usePageLeave
- useParallax
- usePerformanceObserver
- useResizeObserver
- useScroll
- useScrollIntoView
- useScrollTo
- useSwipe
- useTextSelection
- useVisibility
- useWindowEvent
- useWindowFocus
- useWindowScroll
- useWindowSize
DT
Design Team23:05
Daria: pushed the new icons 🎨3
AC
Dmitry Babin12:03
See you at the meeting tomorrow
RU
reactuse11:48
You: just shipped useResizeObserver1
import type { PointerEvent } from 'react';
import { useResizeObserver } from '@siberiacancode/reactuse';
import { GripVerticalIcon } from 'lucide-react';
import { useRef, useState } from 'react';
import { cn } from '@/utils/lib';
interface Chat {
id: string;
initials: string;
message: string;
name: string;
online?: boolean;
time: string;
unread?: number;
}
const CHATS: Chat[] = [
{
id: '1',
name: 'Design Team',
message: 'Daria: pushed the new icons 🎨',
time: '23:05',
initials: 'DT',
unread: 3,
online: true
},
{
id: '2',
name: 'Dmitry Babin',
message: 'See you at the meeting tomorrow',
time: '12:03',
initials: 'AC',
online: true
},
{
id: '3',
name: 'reactuse',
message: 'You: just shipped useResizeObserver',
time: '11:48',
initials: 'RU',
unread: 1
}
];
const COLLAPSED_WIDTH = 66;
const EXPANDED_MIN = 240;
const EXPANDED_MAX = 420;
const SNAP_POINT = (COLLAPSED_WIDTH + EXPANDED_MIN) / 2;
const getEntryWidth = (entry: ResizeObserverEntry) =>
entry.borderBoxSize[0]?.inlineSize ?? entry.contentRect.width;
const Demo = () => {
const [width, setWidth] = useState(340);
const [expanded, setExpanded] = useState(true);
const draggingRef = useRef(false);
const dragRef = useRef({ startWidth: 340, startX: 0 });
const resizeObserver = useResizeObserver<HTMLDivElement>({
box: 'border-box',
onChange: ([entry]) => setExpanded(getEntryWidth(entry) >= EXPANDED_MIN)
});
const onPointerDown = (event: PointerEvent<HTMLButtonElement>) => {
draggingRef.current = true;
dragRef.current = {
startWidth: width,
startX: event.clientX
};
event.currentTarget.setPointerCapture(event.pointerId);
};
const onPointerMove = (event: PointerEvent<HTMLButtonElement>) => {
if (!draggingRef.current) return;
const rawWidth = Math.min(
EXPANDED_MAX,
Math.max(COLLAPSED_WIDTH, dragRef.current.startWidth + event.clientX - dragRef.current.startX)
);
setWidth(rawWidth < SNAP_POINT ? COLLAPSED_WIDTH : Math.max(EXPANDED_MIN, rawWidth));
};
const onPointerUp = (event: PointerEvent<HTMLButtonElement>) => {
draggingRef.current = false;
event.currentTarget.releasePointerCapture(event.pointerId);
};
return (
<section className='flex flex-col items-center gap-3 p-4'>
<div className='relative'>
<div
ref={resizeObserver.ref}
className='bg-card no-scrollbar flex flex-col overflow-hidden rounded-2xl'
style={{ width }}
>
{CHATS.map((chat) => (
<div
key={chat.id}
className={cn(
'hover:bg-muted/50 flex cursor-pointer items-center gap-3 py-2.5 transition-colors',
expanded ? 'px-3' : 'justify-center px-0'
)}
>
<div className='relative shrink-0'>
<div data-size='lg' data-slot='avatar'>
<span data-slot='avatar-fallback'>{chat.initials}</span>
</div>
{chat.online && (
<span className='ring-card absolute right-0 bottom-0 size-3 rounded-full bg-green-500 ring-2' />
)}
{!expanded && !!chat.unread && (
<span className='bg-primary text-primary-foreground absolute -top-1 -right-1 flex size-4 items-center justify-center rounded-full text-[10px] font-semibold'>
{chat.unread}
</span>
)}
</div>
{expanded && (
<div className='flex min-w-0 flex-1 flex-col'>
<div className='flex items-center justify-between gap-2'>
<span className='text-foreground truncate text-sm font-medium'>
{chat.name}
</span>
<span className='text-muted-foreground shrink-0 text-xs'>{chat.time}</span>
</div>
<div className='flex items-center justify-between gap-2'>
<span className='text-muted-foreground truncate text-xs'>{chat.message}</span>
{!!chat.unread && (
<span className='bg-primary text-primary-foreground flex size-4 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold'>
{chat.unread}
</span>
)}
</div>
</div>
)}
</div>
))}
</div>
<button
aria-label='Resize'
className='border-border bg-card text-muted-foreground hover:text-foreground absolute top-1/2 -right-3 z-10 flex size-6 -translate-y-1/2 cursor-ew-resize touch-none items-center justify-center rounded-full border shadow-sm select-none'
data-variant='unstyled'
type='button'
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
>
<GripVerticalIcon className='size-3.5' />
</button>
</div>
</section>
);
};
export default Demo;
This hook uses ResizeObserver browser api to provide enhanced functionality. Make sure to check for compatibility with different browsers when using this api
Installation
pnpm add @siberiacancode/reactuseUsage
const { entries, observer } = useResizeObserver(ref);
// or
const { ref, entries, observer } = useResizeObserver();
// or
const { ref, entries, observer } = useResizeObserver((entries) => console.log(entries));
// or
const { entries, observer } = useResizeObserver(ref, (entries) => console.log(entries));Type Declarations
import type { HookTarget } from '@/utils/helpers';
import type { StateRef } from '../useRefState/useRefState';
export type UseResizeObserverCallback = (
entries: ResizeObserverEntry[],
observer: ResizeObserver
) => void;
export interface UseResizeObserverOptions extends ResizeObserverOptions {
/** The enabled state of the resize observer */
enabled?: boolean;
/** The callback to execute when resize is detected */
onChange?: UseResizeObserverCallback;
}
export interface UseResizeObserverReturn {
/** The resize observer entries */
entries?: ResizeObserverEntry[];
/** The resize observer instance */
observer?: ResizeObserver;
}
export interface UseResizeObserver {
<Target extends Element>(
options?: UseResizeObserverOptions,
target?: never
): UseResizeObserverReturn & { ref: StateRef<Target> };
(target: HookTarget, options?: UseResizeObserverOptions): UseResizeObserverReturn;
<Target extends Element>(
callback: UseResizeObserverCallback,
target?: never
): UseResizeObserverReturn & { ref: StateRef<Target> };
(target: HookTarget, callback: UseResizeObserverCallback): UseResizeObserverReturn;
}API
Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| target | HookTarget | - | The target element to observe |
| options.enabled | boolean | true | The enabled state of the resize observer |
| options.box | ResizeObserverBoxOptions | - | The box model to observe |
| options.onChange | UseResizeObserverCallback | - | The callback to execute when resize is detected |
Returns
UseResizeObserverReturnParameters
| Name | Type | Default | Note |
|---|---|---|---|
| options.enabled | boolean | true | The enabled state of the resize observer |
| options.box | ResizeObserverBoxOptions | - | The box model to observe |
| options.onChange | UseResizeObserverCallback | - | The callback to execute when resize is detected |
Returns
UseResizeObserverReturn & { ref: StateRef<Target> }Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| callback | UseResizeObserverCallback | - | The callback to execute when resize is detected |
Returns
UseResizeObserverReturn & { ref: StateRef<Target> }Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| target | HookTarget | - | The target element to observe |
| callback | UseResizeObserverCallback | - | The callback to execute when resize is detected |
Returns
UseResizeObserverReturnContributors
ddebabin
Last updated on