GooseUI

Scroll To Top

A floating button that appears after scrolling and smoothly scrolls the page back to the top. Available in 4 variants: default circle, pill with text, minimal icon-only, and progress indicator.

Preview

Default
Pill
Minimal
Progress (50%)
Text (50%)

Installation

npm
pnpm
yarn
bun
npx shadcn@latest add @gooseui/scroll-to-top

Usage

import { ScrollToTop } from "@/components/ui/scroll-to-top"

export default function Page() {
  return (
    <main>
      {/* Your page content */}
      <ScrollToTop />
    </main>
  )
}

Variants

Default

Circular button with arrow icon. Supports 3 sizes: sm, md, lg.

import { ScrollToTop } from "@/components/ui/scroll-to-top"

<ScrollToTop />
<ScrollToTop size="sm" />
<ScrollToTop size="lg" />

Pill

Elongated button with customizable text label.

import { ScrollToTopPill } from "@/components/ui/scroll-to-top"

<ScrollToTopPill />
<ScrollToTopPill label="Back to top" />

Minimal

Icon-only button without background. Subtle and unobtrusive.

import { ScrollToTopMinimal } from "@/components/ui/scroll-to-top"

<ScrollToTopMinimal />

Progress

Circular button with scroll progress indicator. Shows how far user has scrolled on the page.

import { ScrollToTopProgress } from "@/components/ui/scroll-to-top"

<ScrollToTopProgress />
<ScrollToTopProgress progressColor="#10b981" />
<ScrollToTopProgress strokeWidth={4} />

Examples

Custom Threshold

Adjust the scroll distance before the button appears.

// Button appears after scrolling 500px
<ScrollToTop threshold={500} />

// Button appears after scrolling 100px
<ScrollToTop threshold={100} />

Custom Position

Override default positioning with custom classes.

// Bottom left corner
<ScrollToTop className="left-6 right-auto" />

// Higher position
<ScrollToTop className="bottom-20" />

Using the Hook

Use the useScrollToTop hook for custom implementations.

import { useScrollToTop } from "@/components/ui/scroll-to-top"

function CustomScrollButton() {
  const { isVisible, scrollProgress, scrollToTop } = useScrollToTop(300)

  if (!isVisible) return null

  return (
    <button onClick={() => scrollToTop()}>
      {Math.round(scrollProgress)}% scrolled - Click to go up
    </button>
  )
}

Props

ScrollToTop (Default)

PropTypeDefaultDescription
thresholdnumber300Scroll distance (px) before button appears
behaviorScrollBehavior"smooth"Scroll animation behavior
size"sm" | "md" | "lg""md"Button size
classNamestring-Additional CSS classes

ScrollToTopPill

PropTypeDefaultDescription
labelstring"Scroll to top"Button text label
thresholdnumber300Scroll distance (px) before button appears
classNamestring-Additional CSS classes

ScrollToTopProgress

PropTypeDefaultDescription
progressColorstringprimaryProgress circle color
progressBgColorstringmutedProgress background circle color
strokeWidthnumber3Circle stroke width in px
thresholdnumber300Scroll distance (px) before button appears

useScrollToTop Hook

Use the hook for custom scroll-to-top implementations:

import { useScrollToTop } from "@/components/ui/scroll-to-top"

function MyComponent() {
  const {
    isVisible,     // boolean - whether threshold is passed
    scrollProgress, // number 0-100 - scroll progress percentage
    scrollToTop,    // (behavior?: ScrollBehavior) => void
  } = useScrollToTop(300) // threshold in px

  return (
    <div>
      <p>Progress: {scrollProgress.toFixed(0)}%</p>
      {isVisible && (
        <button onClick={() => scrollToTop("smooth")}>
          Go to top
        </button>
      )}
    </div>
  )
}

Features

  • 4 Variants — default, pill, minimal, and progress styles
  • Smooth animations — fade and slide transitions on show/hide
  • Configurable threshold — control when button appears
  • Progress indicator — visual scroll progress feedback
  • Accessible — proper aria-label and focus states
  • Hook export — build custom implementations
  • Customizable — override position, colors, and more via className