Back to Top Button — Free HTML CSS JS Scroll-to-Top Snippet
Back to Top Button · Scroll · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="page" id="page">
<div class="hero">
<h1>Scroll down to see the button appear</h1>
<p>Keep scrolling — a floating button will fade in once you pass the threshold, and smooth-scroll you back to the top on click.</p>
</div>
<section class="block" id="s1"><h2>Section One</h2><p>Long pages benefit from a quick way back to the top instead of forcing a slow reverse scroll through everything already read.</p></section>
<section class="block alt" id="s2"><h2>Section Two</h2><p>The button stays hidden until the user has scrolled past a threshold, so it never clutters the initial view of a short page.</p></section>
<section class="block" id="s3"><h2>Section Three</h2><p>A smooth scroll animation on click feels more intentional than an instant jump, especially on pages with a lot of vertical distance to cover.</p></section>
<section class="block alt" id="s4"><h2>Section Four</h2><p>Pair this with a scroll-progress indicator so returning users can tell at a glance how much of the page is left.</p></section>
<button class="back-to-top" id="backToTop" onclick="scrollToTop()" aria-label="Back to top">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>
</button>
</div>* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; }
.page { position: relative; }
.hero {
padding: 80px 24px;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: #fff;
text-align: center;
}
.hero h1 { font-size: 24px; margin: 0 0 10px; }
.hero p { font-size: 14px; opacity: 0.9; max-width: 460px; margin: 0 auto; line-height: 1.6; }
.block { padding: 80px 24px; background: #fff; }
.block.alt { background: #f8fafc; }
.block h2 { font-size: 20px; color: #1e293b; margin: 0 0 10px; max-width: 560px; margin-inline: auto; }
.block p { font-size: 14px; color: #64748b; line-height: 1.7; max-width: 560px; margin: 0 auto; }
.back-to-top {
position: fixed;
bottom: 28px;
right: 28px;
width: 46px;
height: 46px;
border-radius: 50%;
border: none;
background: #1e293b;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.3);
opacity: 0;
visibility: hidden;
transform: translateY(12px);
transition: opacity 0.25s ease, transform 0.25s ease, background 0.15s ease;
}
.back-to-top.visible {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.back-to-top:hover { background: #6366f1; }
.back-to-top:focus-visible { outline: 2px solid #6366f1; outline-offset: 3px; }const backToTop = document.getElementById('backToTop');
const SHOW_AFTER = 400;
window.addEventListener('scroll', () => {
if (window.scrollY > SHOW_AFTER) {
backToTop.classList.add('visible');
} else {
backToTop.classList.remove('visible');
}
});
function scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' });
}Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Back to Top Button — HTML, CSS & JavaScript Scroll-to-Top Snippet

On long pages — articles, product listings, changelogs — users who scroll far down need a fast way back to the header without repeatedly flicking their mouse wheel or dragging a thumb across a phone screen. A back-to-top button solves this with a single fixed-position control that appears once it's actually useful and disappears when it isn't.
This snippet implements the full pattern in plain HTML, CSS, and vanilla JavaScript: a circular button fixed to the bottom-right corner, hidden by default, that fades and slides into view once the page has been scrolled past a threshold, and smooth-scrolls back to the top when clicked.
How the show/hide threshold works
A single scroll event listener on window checks window.scrollY against a SHOW_AFTER constant (400px by default). Past that point, the .visible class is added to the button; below it, the class is removed. The class itself does the animating — opacity, visibility, and a small transform: translateY all transition over 0.25s, so the button fades and slides up into place rather than snapping in abruptly. visibility: hidden is paired with opacity: 0 specifically so the invisible button can't be tabbed to or accidentally clicked while hidden.
How the smooth scroll works
The click handler is a single line: window.scrollTo({ top: 0, behavior: 'smooth' }). This uses the native Scroll Behavior API rather than a manual requestAnimationFrame loop or an interval that decrements scrollTop — the browser handles the easing curve itself, and it respects the user's OS-level "reduce motion" accessibility setting automatically in supporting browsers.
Why the threshold matters
Showing the button immediately at the top of the page is pointless — there's nowhere to scroll to. A 400px threshold roughly corresponds to "past the hero section" on most page layouts, so the button only appears once a user has actually scrolled far enough that jumping back up saves real effort. Tune SHOW_AFTER per page: a value close to one viewport height works well for most designs.
Performance note
The scroll listener here does very little work per tick — one comparison and a class toggle — so it doesn't need throttling for typical use. On extremely scroll-heavy pages with many other listeners, you can wrap the check in a requestAnimationFrame guard to avoid running it more than once per frame.
Step by step
How to Use
- 1Load the snippetClick "Back to Top Button" in the sidebar Library tab. The preview loads with a tall scrollable page and a hidden button.
- 2Scroll down in the previewScroll past the hero section — the circular arrow button fades and slides in from the bottom-right corner.
- 3Click the buttonClick it to trigger a native smooth scroll back to the top of the page, then watch the button fade back out.
- 4Adjust the thresholdIn the JS panel, change the SHOW_AFTER constant to control how far a user must scroll before the button appears.
- 5Restyle the buttonIn the CSS panel, update .back-to-top background, size, and position to match your site — bottom-left placement just needs left instead of right.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for React, or "Tailwind" for React + Tailwind CSS.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A scroll event listener on window checks window.scrollY against a SHOW_AFTER constant (400px by default). Once the user has scrolled past that point, a .visible class is added, which fades and slides the button into view via CSS transition.
Edit the SHOW_AFTER constant near the top of the JS panel. A larger number delays the button appearing until further down the page; a smaller number shows it sooner.
window.scrollTo with behavior: "smooth" is supported in all current major browsers. For very old browsers without support, the scroll simply happens instantly instead of animated — it still functions correctly, just without the easing.
Yes. In the CSS panel, replace right: 28px with left: 28px on .back-to-top. Everything else — the fade animation, the threshold logic — is unaffected by its horizontal position.
Only if they occupy the same corner. Move the button to a different corner, or add extra bottom/right spacing to stack it above another fixed element without overlapping.
Yes. It stays out of the tab order while hidden thanks to visibility: hidden, and once visible it is a normal focusable button with a visible focus-visible outline, so it works with keyboard-only and screen reader navigation.
Yes. Add an SVG circle stroke behind the arrow icon and update its stroke-dashoffset in the scroll listener proportionally to window.scrollY divided by the page's total scrollable height, turning the button into a scroll-progress indicator.
opacity: 0 alone still leaves the element clickable and focusable even though it is invisible. Pairing it with visibility: hidden removes it from the tab order and from hit-testing while hidden, then visibility: visible restores both once the .visible class is applied.