Fiber UI LogoFiberUI

useWindowSize

A React hook that tracks the window dimensions.

Installation

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

Features

  • Responsive Updates - Updates width and height on window resize.
  • Debounced - Can be easily debounced (see example).
  • SSR Safe - Returns 0 or initial values during server-side rendering to match hydration.

Source Code

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

Interactive Example

Window Dimensions

Resize your browser window to see updates.

Width0px
Height0px
Current Viewport: Mobile (< 768px)
"use client";

import { useWindowSize } from "@repo/hooks/dom/use-window-size";
import { Maximize2 } from "lucide-react";

export const Example1 = () => {
    const { width, height } = useWindowSize();
    const isMobile = width < 768;

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

            <p className="text-muted-foreground text-sm">
                Resize your browser window to see updates.
            </p>

            <div className="bg-muted/50 grid grid-cols-2 gap-px overflow-hidden rounded-lg border">
                <div className="bg-background flex flex-col items-center justify-center p-6">
                    <span className="text-muted-foreground mb-1 text-xs uppercase tracking-wider">
                        Width
                    </span>
                    <span className="text-primary font-mono text-3xl font-bold">
                        {width}
                    </span>
                    <span className="text-muted-foreground text-xs">px</span>
                </div>
                <div className="bg-background flex flex-col items-center justify-center p-6">
                    <span className="text-muted-foreground mb-1 text-xs uppercase tracking-wider">
                        Height
                    </span>
                    <span className="text-primary font-mono text-3xl font-bold">
                        {height}
                    </span>
                    <span className="text-muted-foreground text-xs">px</span>
                </div>
            </div>

            <div
                className={`rounded-md px-3 py-2 text-center text-sm font-medium transition-colors ${
                    isMobile
                        ? "bg-blue-500/10 text-blue-600"
                        : "bg-green-500/10 text-green-600"
                }`}
            >
                Current Viewport: {isMobile ? "Mobile" : "Desktop"} (
                {width < 768 ? "<" : "≄"} 768px)
            </div>
        </div>
    );
};
const { width, height } = useWindowSize();
const isMobile = width < 768;

Responsive Layout

Renders different components based on window width.

Responsive Component Switching

mobile Layout

0px768px1024pxāˆž
MobileTabletDesktop

Resize the window to switch the rendered icon and layout logic.

"use client";

import { useWindowSize } from "@repo/hooks/dom/use-window-size";
import { Laptop, Smartphone, Tablet } from "lucide-react";

export const Example2 = () => {
    const { width } = useWindowSize();

    let deviceType = "desktop";
    let Icon = Laptop;
    let color = "text-blue-500";

    if (width < 768) {
        deviceType = "mobile";
        Icon = Smartphone;
        color = "text-green-500";
    } else if (width < 1024) {
        deviceType = "tablet";
        Icon = Tablet;
        color = "text-orange-500";
    }

    return (
        <div className="flex w-full max-w-md flex-col items-center gap-6">
            <h3 className="font-semibold">Responsive Component Switching</h3>

            <div className="flex flex-col items-center gap-2">
                <Icon
                    className={`h-16 w-16 ${color} transition-all duration-300`}
                />
                <p className="font-medium capitalize">{deviceType} Layout</p>
            </div>

            <div className="w-full space-y-2">
                <div className="text-muted-foreground flex justify-between text-xs">
                    <span>0px</span>
                    <span>768px</span>
                    <span>1024px</span>
                    <span>āˆž</span>
                </div>
                <div className="bg-muted h-2 w-full overflow-hidden rounded-full">
                    <div
                        className="bg-primary h-full transition-all duration-300"
                        style={{
                            width: `${Math.min((width / 1200) * 100, 100)}%`,
                        }}
                    />
                </div>
                <div className="flex justify-between font-mono text-xs">
                    <span
                        className={
                            width < 768
                                ? "font-bold text-green-600"
                                : "text-muted-foreground"
                        }
                    >
                        Mobile
                    </span>
                    <span
                        className={
                            width >= 768 && width < 1024
                                ? "font-bold text-orange-600"
                                : "text-muted-foreground"
                        }
                    >
                        Tablet
                    </span>
                    <span
                        className={
                            width >= 1024
                                ? "font-bold text-blue-600"
                                : "text-muted-foreground"
                        }
                    >
                        Desktop
                    </span>
                </div>
            </div>

            <p className="text-muted-foreground text-center text-sm">
                Resize the window to switch the rendered icon and layout logic.
            </p>
        </div>
    );
};
const { width } = useWindowSize();

if (width < 768) return <MobileLayout />;
return <DesktopLayout />;

API Reference

Signature

const { width, height } = useWindowSize();

Returns

PropertyTypeDescription
widthnumberWindow width in pixels.
heightnumberWindow height in pixels.

Hook Source Code

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

interface WindowSize {
    width: number;
    height: number;
}

const isBrowser = typeof window !== "undefined";

/**
 * useWindowSize - Track window dimensions
 *
 * @returns Object with width and height of the window
 *
 * @example
 * const { width, height } = useWindowSize();
 */
export function useWindowSize(): WindowSize {
    const [size, setSize] = useState<WindowSize>(() => ({
        width: isBrowser ? window.innerWidth : 0,
        height: isBrowser ? window.innerHeight : 0,
    }));

    const handleResize = useCallback(() => {
        setSize({
            width: window.innerWidth,
            height: window.innerHeight,
        });
    }, []);

    useEventListener("resize", handleResize);

    return size;
}