Skip to content

NavigationBack to top

Back to top

A floating arrow-up button that appears in the bottom-right after 1,200 px of scroll. Smooth-scrolls to the top.

1,200 px thresholdSmooth scrollFixed bottom-right

Demo

The button is 44 × 44 px, fixed to the bottom-right, with a subtle shadow. It is hidden until the user has scrolled past 1,200 px.

Default

Anatomy

  • 44 × 44 px circle, --foreground background, --background icon
  • Fixed bottom-right, 16 px from the edges on mobile, 24 px on desktop
  • Shadow: elevation-2
  • Appears after 1,200 px of scroll, with a 200 ms ease-out fade-in

Implementation

back-to-top.tsx
"use client"

import { useEffect, useState } from "react"
import { ArrowUp } from "lucide-react"
import SiteIcon from "@/components/site-icon"

interface BackToTopProps {
  threshold?: number
  smooth?: boolean
}

export function BackToTop({ threshold = 1200, smooth = true }: BackToTopProps) {
  const [visible, setVisible] = useState(false)
  useEffect(() => {
    const onScroll = () => setVisible(window.scrollY > threshold)
    onScroll()
    window.addEventListener("scroll", onScroll, { passive: true })
    return () => window.removeEventListener("scroll", onScroll)
  }, [threshold])
  return (
    <button
      type="button"
      className={`back-to-top ${visible ? "back-to-top--visible" : ""}`}
      onClick={() => window.scrollTo({ top: 0, behavior: smooth ? "smooth" : "auto" })}
      aria-label="Back to top"
    >
      <SiteIcon icon={ArrowUp} size="md" />
    </button>
  )
}

Props

PropTypeDefaultDescription
thresholdnumber1200Show after this many px of scroll
smoothbooleantrueSmooth scroll vs instant