Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
import { useState, useEffect, useCallback, useRef } from "react";
interface WindowSize {
width: number;
height: number;
}
interface UseWindowSizeOptions {
/** Atraso do debounce em ms. Padrão: 100 */
debounceDelay?: number;
/** Tamanho inicial para SSR. Padrão: { width: 0, height: 0 } */
initialSize?: WindowSize;
}
function useWindowSize(options: UseWindowSizeOptions = {}): WindowSize {
const {
debounceDelay = 100,
initialSize = { width: 0, height: 0 },
} = options;
const [size, setSize] = useState<WindowSize>(() => {
// Inicializador preguiçoso: lê as dimensões da janela apenas no cliente
if (typeof window === "undefined") return initialSize;
return {
width: window.innerWidth,
height: window.innerHeight,
};
});
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
// Não faz nada no servidor
if (typeof window === "undefined") return;
const handleResize = () => {
// Limpa qualquer timer pendente
if (timerRef.current) clearTimeout(timerRef.current);
// Define um novo timer para atualizar o tamanho após o atraso do debounce
timerRef.current = setTimeout(() => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
timerRef.current = null; // Limpa a referência do timer após a execução
}, debounceDelay);
};
// Define o tamanho inicial ao montar para garantir que o estado esteja atualizado
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
// Adiciona o listener de evento de redimensionamento
window.addEventListener("resize", handleResize);
// Função de limpeza para remover o listener e limpar o timer
return () => {
window.removeEventListener("resize", handleResize);
if (timerRef.current) clearTimeout(timerRef.current);
};
}, [debounceDelay]); // Reexecuta o efeito se debounceDelay mudar
return size;
}Quando usar isso: Você precisa das dimensões da janela em JavaScript para cálculos de layout responsivo, dimensionamento de tela, listas virtualizadas ou renderização condicional que as media queries CSS não conseguem lidar.
"use client";
function ResponsiveLayout() {
const { width, height } = useWindowSize();
// Determina o número de colunas com base na largura da janela
const columns = width >= 1024 ? 3 : width >= 640 ? 2 : 1;
return (
<div>
<p>
Janela: {width} x {height}
</p>
<div
style={{
display: "grid",
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap: 16,
}}
>
{Array.from({ length: 6 }, (_, i) => (
<div
key={i}
style={{
padding: 24,
background: "#f5f5f5",
borderRadius: 8,
textAlign: "center",
}}
>
Card {i + 1}
</div>
))}
</div>
</div>
);
}
function CanvasSizer() {
// Usa um atraso de debounce maior para o componente de tela
const { width } = useWindowSize({ debounceDelay: 200 });
// Calcula a largura e altura da tela mantendo a proporção 16:9
const canvasWidth = Math.min(width - 32, 800);
const canvasHeight = canvasWidth * 0.5625; // Proporção 16:9
return (
<canvas
width={canvasWidth}
height={canvasHeight}
style={{ border: "1px solid #ccc" }}
/>
);
}O que isso demonstra:
useState lê window.innerWidth e window.innerHeight apenas no cliente, retornando initialSize durante o SSR.setTimeout colapsa eventos rápidos de redimensionamento em uma única atualização de estado, prevenindo travamentos.| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
debounceDelay | number | 100 | Milissegundos para debouncing de eventos de redimensionamento |
initialSize | { width, height } | { width: 0, height: 0 } | Tamanho retornado durante o SSR |
| Retorno | Tipo | Descrição |
|---|---|---|
width | number | window.innerWidth atual |
height | number | window.innerHeight atual |
Sem debounce: Para atualizações imediatas (por exemplo, pré-visualizações de redimensionamento por arrasto), defina debounceDelay: 0 ou remova o setTimeout:
const size = useWindowSize({ debounceDelay: 0 });Com orientação: Detecte paisagem vs. retrato:
function useOrientation() {
const { width, height } = useWindowSize();
return width > height ? "landscape" : "portrait";
}Tamanho do documento (altura de rolagem): Acompanhe a altura total do documento em vez da viewport:
// Dentro do manipulador de redimensionamento:
setSize({
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight,
});WindowSize é exportada para que os consumidores possam tipar seu próprio estado ou props.{ width: number; height: number }.initialSize (0x0), mas o cliente atualiza imediatamente para as dimensões reais. Correção: Isso causa uma mudança de layout na primeira renderização. Para layouts críticos, prefira media queries CSS ou forneça uma estimativa razoável de initialSize.debounceDelay conforme necessário.window.visualViewport se precisar distinguir o teclado de um redimensionamento real da janela.window.innerWidth reflete o tamanho do iframe, não da janela pai. Correção: Use parent.window se a política de origem cruzada permitir, ou passe o tamanho como uma prop.| Pacote | Nome do Hook | Notas |
|---|---|---|
usehooks-ts | useWindowSize | Sem debounce integrado |
@uidotdev/usehooks | useWindowSize | Implementação mínima |
ahooks | useSize | Rastreia qualquer elemento, não apenas a janela |
react-use | useWindowSize | Inclui padrões do lado do servidor |
@react-hook/window-size | useWindowSize | Variante com throttling disponível |
Sem debounce, cada pixel de um arrasto de redimensionamento aciona uma atualização de estado e re-renderização. O debounce integrado colapsa eventos rápidos em uma única atualização, prevenindo travamentos e renderizações desperdiçadas.
Defina debounceDelay para 0:
const size = useWindowSize({ debounceDelay: 0 });Isso ainda usa setTimeout(..., 0) que adia para o próximo tick. Para atualizações verdadeiramente síncronas, remova o setTimeout do hook.
initialSize fornece as dimensões retornadas durante o SSR (padrão: { width: 0, height: 0 }).{ width: 1024, height: 768 }) para reduzir a mudança de layout.O componente pode ter sido desmontado e remontado enquanto a janela era redimensionada. A definição imediata garante que o estado esteja correto, mesmo que nenhum evento de redimensionamento ocorra após a montagem.
initialSize realista que corresponda à sua viewport mais comum.useMediaQuery para verificações booleanas de ponto de interrupção que toleram o breve flash.Em navegadores móveis, o teclado virtual redimensiona a viewport. O hook dispara em qualquer redimensionamento, incluindo a abertura/fechamento do teclado. Use a API window.visualViewport para distinguir eventos de teclado de redimensionamentos reais da janela.
useWindowSize retorna valores exatos em pixels (width, height).useMediaQuery retorna um booleano para um ponto de interrupção específico.useWindowSize quando precisar de cálculos (por exemplo, dimensionamento de tela, contagem de colunas).useMediaQuery quando precisar apenas de um alternador booleano.Sim. Substitua window.innerWidth/Height por document.documentElement.scrollWidth/Height dentro do manipulador de redimensionamento. Isso fornece o tamanho total do documento, incluindo o overflow.
O hook retorna WindowSize, que é { width: number; height: number }. A interface é exportada para que os consumidores possam usá-la para seus próprios tipos de props ou estado.
function useOrientation(): "landscape" | "portrait" {
const { width, height } = useWindowSize();
return width > height ? "landscape" : "portrait";
}Revisado por Chris St. John·Última atualização: 10 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥