Skip to main content

Overview

The ScrollToTop component is a floating action button that appears after scrolling, allowing users to quickly return to the top of the page with a smooth scrolling animation.

Features

  • Scroll-Based Visibility: Appears after scrolling 400px down the page
  • Smooth Scrolling: Uses smooth scroll behavior when clicked
  • Smooth Animations: Fade and slide animations for appearance/disappearance
  • Hover Effects: Scale and color change effects on hover
  • Fixed Positioning: Stays centered at the bottom of the viewport
  • Accessible: Includes proper aria-label for screen readers

Component Structure

The ScrollToTop component is a client-side component that doesn’t accept any props.
export default function ScrollToTop()

Implementation

Basic Usage

import ScrollToTop from '@/components/ScrollToTop';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <ScrollToTop />
      </body>
    </html>
  );
}

State Management

The component manages a single piece of state:
const [visible, setVisible] = useState(false);
  • visible: Controls whether the button is shown based on scroll position

Scroll Detection

The component listens for scroll events and shows the button after 400px:
useEffect(() => {
  const toggleVisibility = () => {
    setVisible(window.scrollY > 400);
  };

  window.addEventListener("scroll", toggleVisibility);
  return () => window.removeEventListener("scroll", toggleVisibility);
}, []);

Scroll to Top Function

When clicked, the button smoothly scrolls to the top of the page:
const scrollToTop = () => {
  window.scrollTo({
    top: 0,
    behavior: "smooth",
  });
};

Button Implementation

<button
  onClick={scrollToTop}
  className={`
    fixed bottom-6 left-1/2 -translate-x-1/2 z-50
    w-10 h-10 rounded-full
    bg-[#163594] text-white
    shadow-lg
    flex items-center justify-center
    cursor-pointer
    transition-all duration-300
    hover:scale-110 hover:bg-blue-700
    ${visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10 pointer-events-none"}
  `}
  aria-label="Volver arriba"
>
  {/* SVG arrow icon */}
</button>

Styling

Button Appearance

  • Size: 40px x 40px
  • Color: Brand blue (#163594)
  • Shape: Circular with rounded-full
  • Icon: Upward-pointing arrow SVG

Animation Classes

className={`
  fixed bottom-6 left-1/2 -translate-x-1/2 z-50
  w-10 h-10 rounded-full
  bg-[#163594] text-white
  shadow-lg
  flex items-center justify-center
  cursor-pointer
  transition-all duration-300
  hover:scale-110 hover:bg-blue-700
  ${visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10 pointer-events-none"}
`}

Visibility States

Visible

  • opacity-100: Fully visible
  • translate-y-0: Original position
  • Pointer events enabled

Hidden

  • opacity-0: Invisible
  • translate-y-10: Moved 40px down
  • pointer-events-none: No interaction possible

Hover Effects

  • hover:scale-110: Grows to 110% size
  • hover:bg-blue-700: Darker blue background

Positioning

The button is centered horizontally at the bottom of the viewport:
fixed              // Fixed positioning
bottom-6           // 24px from bottom
left-1/2           // Centered horizontally
-translate-x-1/2   // Offset to true center
z-50               // High z-index to stay on top

SVG Arrow Icon

The component uses an inline SVG for the upward arrow:
<svg
  xmlns="http://www.w3.org/2000/svg"
  className="w-5 h-5"
  fill="none"
  viewBox="0 0 24 24"
  stroke="currentColor"
  strokeWidth={2}
>
  <path
    strokeLinecap="round"
    strokeLinejoin="round"
    d="M5 15l7-7 7 7"
  />
</svg>

Animation Duration

All transitions use a 300ms duration for quick, responsive animations:
transition-all duration-300

Accessibility

The button includes proper accessibility attributes:
aria-label="Volver arriba"  // Screen reader label
cursor-pointer              // Visual cursor feedback
The button uses pointer-events-none when hidden to prevent accidental clicks on the invisible element.

Dependencies

import { useEffect, useState } from "react";
This component has no external dependencies beyond React, making it lightweight and easy to integrate.

Design Tokens

Colors

  • Background: #163594 (Brand blue)
  • Hover Background: bg-blue-700
  • Text: White

Timing

  • Scroll Threshold: 400px
  • Transition Duration: 300ms
  • Scroll Behavior: Smooth

Sizes

  • Button: 40px x 40px
  • Icon: 20px x 20px
  • Hover Scale: 110%

Customization Examples

Change Scroll Threshold

const toggleVisibility = () => {
  setVisible(window.scrollY > 600); // Show after 600px instead
};

Change Button Color

bg-[#your-color] hover:bg-[#your-hover-color]

Change Position

// Move to bottom-right corner
className="fixed bottom-6 right-6 z-50 ..."

// Move to bottom-left corner
className="fixed bottom-6 left-6 z-50 ..."

Instant Scroll (No Animation)

const scrollToTop = () => {
  window.scrollTo({
    top: 0,
    behavior: "auto", // Changed from "smooth"
  });
};

Change Button Size

// Larger button
className="w-12 h-12 ..."  // 48px x 48px

// Smaller button
className="w-8 h-8 ..."    // 32px x 32px

Browser Support

The smooth scroll behavior is supported in all modern browsers. For older browsers, it falls back to instant scrolling.

Scroll Behavior Support

  • Chrome: 61+
  • Firefox: 36+
  • Safari: 15.4+
  • Edge: 79+

Performance Considerations

The component uses efficient scroll event handling:
  • Event listener is properly cleaned up on unmount
  • Simple boolean state toggle (no expensive calculations)
  • CSS transitions handled by the browser

Source Code Location

/app/components/ScrollToTop.tsx

Build docs developers (and LLMs) love