Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
import { useState, useEffect, useCallback } from "react";
interface UseScrollToTopOptions {
/** Distância de rolagem (px) antes do botão aparecer. Padrão: 300 */
threshold?: number;
/** Usa rolagem suave. Padrão: true */
smooth?: boolean;
}
interface UseScrollToTopReturn {
/** Se a página rolou além do limite */
isVisible: boolean;
/** Chame isso para rolar até o topo */
scrollToTop: () => void;
/** Posição atual de rolagem */
scrollY: number;
}
function useScrollToTop(
options: UseScrollToTopOptions = {}
): UseScrollToTopReturn {
const { threshold = 300, smooth = true } = options;
const [isVisible, setIsVisible] = useState(false);
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
const y = window.scrollY;
setScrollY(y);
setIsVisible(y > threshold);
};
// Verifica a posição inicial
handleScroll();
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, [threshold]);
const scrollToTop = useCallback(() => {
window.scrollTo({
top: 0,
behavior: smooth ? "smooth" : "instant",
});
}, [smooth]);
return { isVisible, scrollToTop, scrollY };
}Quando usar isso: Você tem uma página longa e quer dar aos usuários uma maneira rápida de retornar ao topo, com o botão aparecendo apenas depois que eles rolarem uma distância significativa.
"use client";
function BackToTopButton() {
const { isVisible, scrollToTop } = useScrollToTop({
threshold: 400,
smooth: true,
});
return (
<button
onClick={scrollToTop}
aria-label="Scroll to top"
style={{
position: "fixed",
bottom: 24,
right: 24,
width: 48,
height: 48,
borderRadius: "50%",
border: "none",
background: "#111",
color: "#fff",
fontSize: 20,
cursor: "pointer",
opacity: isVisible ? 1 : 0,
transform: isVisible ? "translateY(0)" : "translateY(16px)",
transition: "opacity 0.3s, transform 0.3s",
pointerEvents: isVisible ? "auto" : "none",
}}
>
↑
</button>
);
}
function LongPage() {
return (
<div>
<h1>Título do Artigo</h1>
{Array.from({ length: 50 }, (_, i) => (
<p key={i}>Parágrafo {i + 1} de conteúdo...</p>
))}
<BackToTopButton />
</div>
);
}O que isso demonstra:
pointerEvents: "none" impede que o botão invisível bloqueie cliquesaria-label garante acessibilidade para leitores de telascroll (com { passive: true } para desempenho) rastreia window.scrollY.isVisible muda para true quando scrollY excede o threshold, dando ao componente consumidor um booleano reativo para renderização.scrollToTop chama window.scrollTo com behavior: "smooth" para uma animação de rolagem suave nativa.passive: true informa ao navegador que o manipulador não chamará preventDefault, permitindo otimizações de desempenho de rolagem.useEffect para que o estado do botão esteja correto na montagem (por exemplo, se o usuário atualizar a página no meio dela).| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
options.threshold | number | 300 | Pixels rolados antes de isVisible ser true |
options.smooth | boolean | true | Se deve usar o comportamento de rolagem suave |
| Retorno | Tipo | Descrição |
|---|---|---|
isVisible | boolean | Se a posição de rolagem excede o limite |
scrollToTop | () => void | Função para rolar até o topo |
scrollY | number | Posição Y atual da rolagem |
Manipulador de scroll com throttling: Para páginas com renderização pesada, envolva o manipulador de scroll com useThrottledCallback para reduzir as atualizações de estado:
const handleScroll = useThrottledCallback(() => {
setScrollY(window.scrollY);
setIsVisible(window.scrollY > threshold);
}, 100);Scroll para elemento: Estenda para rolar para qualquer ref de elemento em vez do topo:
const scrollToElement = useCallback((ref: React.RefObject<HTMLElement>) => {
ref.current?.scrollIntoView({ behavior: smooth ? "smooth" : "instant" });
}, [smooth]);requestAnimationFrame.window não está disponível durante a renderização do lado do servidor. Correção: O useEffect só é executado no cliente, então o hook é seguro para SSR como está escrito. O estado padrão (isVisible: false) está correto para SSR.behavior: "smooth". Correção: A página ainda rola instantaneamente, o que é um fallback aceitável.bottom ou adicione z-index para organizar corretamente.| Pacote | Hook/Componente | Notas |
|---|---|---|
react-scroll | animateScroll.scrollToTop() | Biblioteca completa de scroll com componentes de link |
usehooks-ts | useScrollPosition | Rastreia a posição, mas sem função de scroll-to |
ahooks | useScroll | Retorna o estado completo de scroll para qualquer elemento |
framer-motion | useScroll | Rastreamento de scroll focado em animação |
| CSS Nativo | scroll-behavior: smooth | Apenas CSS, sem lógica de botão |
isVisible é true quando window.scrollY excede o valor de threshold.false quando o usuário rola de volta para cima do limite.O flag passive informa ao navegador que o manipulador nunca chamará preventDefault(). Isso permite que o navegador otimize o desempenho da rolagem, não esperando que o manipulador termine antes de rolar.
window.scrollTo({ behavior: "smooth" }) aciona uma animação de rolagem suave nativa.behavior e rolam instantaneamente.Isso lida com o caso em que o usuário atualiza a página já tendo rolado para baixo. Sem a chamada inicial, isVisible permaneceria false até o próximo evento de scroll.
Envolva o manipulador com useThrottledCallback:
const handleScroll = useThrottledCallback(() => {
setScrollY(window.scrollY);
setIsVisible(window.scrollY > threshold);
}, 100);bottom para empurrar o botão acima da barra de navegação.z-index para garantir a organização correta.Se opacity: 0 for definido sem pointerEvents: "none", o botão ainda recebe eventos de clique. O exemplo funcional define pointerEvents: isVisible ? "auto" : "none" para evitar isso.
Sim. O useEffect só é executado no cliente, e os valores de estado padrão (isVisible: false, scrollY: 0) estão corretos para a saída renderizada pelo servidor. Nenhuma verificação typeof window é necessária fora do efeito.
Use as interfaces nomeadas diretamente:
interface Props {
scrollOptions: UseScrollToTopOptions;
}
function MyComponent({ scrollOptions }: Props) {
const result: UseScrollToTopReturn = useScrollToTop(scrollOptions);
}Adicione uma função scrollToElement usando scrollIntoView:
const scrollToElement = useCallback(
(ref: React.RefObject<HTMLElement>) => {
ref.current?.scrollIntoView({
behavior: smooth ? "smooth" : "instant",
});
},
[smooth]
);Revisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥