
Documentation
Webflow
Code
Setup: External Scripts
External Scripts in Webflow
Make sure to always put the External Scripts before the Javascript step of the resource.
In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?
HTML
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrollTrigger.min.js"></script>
Step 1: Copy structure to Webflow
Copy structure to Webflow
In the video below we described how you can copy + paste the structure of this resource to your Webflow project.
Copy to Webflow
Webflow structure is not required for this resource.
Step 1: Add HTML
HTML
<div data-back-to-top="wrap" class="back-top__wrap">
<button data-back-to-top="button" class="back-top__button">
<div class="back-top__arrow-wrap">
<div class="back-top__arrow-row">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 60 60" fill="none" class="back-top__arrow">
<path d="M47.5 25L30 7.5L12.5 25" stroke="currentColor" stroke-width="6" stroke-miterlimit="10" stroke-linecap="round"></path>
<path d="M30 7.5L30 55" stroke="currentColor" stroke-width="6" stroke-miterlimit="10" stroke-linecap="round"></path>
</svg>
</div>
<div class="back-top__arrow-row">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 60 60" fill="none" class="back-top__arrow">
<path d="M47.5 25L30 7.5L12.5 25" stroke="currentColor" stroke-width="6" stroke-miterlimit="10" stroke-linecap="round"></path>
<path d="M30 7.5L30 55" stroke="currentColor" stroke-width="6" stroke-miterlimit="10" stroke-linecap="round"></path>
</svg>
</div>
</div>
</button>
</div>
HTML structure is not required for this resource.
Step 2: Add CSS
CSS
.back-top__wrap {
z-index: 100;
pointer-events: none;
flex-flow: column;
justify-content: flex-end;
align-items: flex-end;
width: 100%;
height: 100vh;
padding: 2em;
display: flex;
position: fixed;
inset: 0%;
}
.back-top__button {
pointer-events: auto;
background-color: #fca5a0;
border: min(.5em, 5px) solid #efeeec;
border-radius: 1em;
outline-style: none;
width: max(5vw, 2.5rem);
height: max(5vw, 2.5rem);
padding: .5em;
position: relative;
}
.back-top__arrow {
width: 2.5em;
}
.back-top__arrow-wrap {
flex-flow: column;
justify-content: flex-start;
align-items: center;
width: 100%;
height: 100%;
display: flex;
position: relative;
overflow: hidden;
}
.back-top__arrow-row {
flex: none;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
display: flex;
}
/* Hide the back-to-top wrapper on default (to prevent flash on loaded site) */
[data-back-to-top="wrap"]{ opacity: 0;}
.back-top__arrow-row{
transition: transform 0.5s cubic-bezier(.65, 0, 0, 1);
}
.back-top__button{
transition: border-width 0.5s cubic-bezier(.65, 0, 0, 1);
}
/* Keyboard focus state */
.back-top__button:focus-visible{
border-width: 0.6em;
}
.back-top__button:focus-visible .back-top__arrow-row{
transform: translate(0px, -100%);
}
/* Hover styling */
@media (hover: hover) {
.back-top__button:hover{
border-width: 0.6em;
}
.back-top__button:hover .back-top__arrow-row{
transform: translate(0px, -100%);
}
}
@media screen and (max-width: 991px) {
.back-top__wrap {
padding: 1.25em;
}
.back-top__button {
border-radius: .5em;
padding: .4em;
}
}
Step 2: Add custom Javascript
Custom Javascript in Webflow
In this video, Ilja gives you some guidance about using JavaScript in Webflow:
Step 2: Add Javascript
Step 3: Add Javascript
Javascript
gsap.registerPlugin(ScrollTrigger);
function initBackToTop() {
const buttonWrap = document.querySelector('[data-back-to-top="wrap"]');
const button = document.querySelector('[data-back-to-top="button"]');
if (!button || !buttonWrap) return;
// The minimum distance the page must be scrolled (in VH) for the button to appear
let minimumScrollDistance = 50;
// Un-do the initial CSS styling to hide the wrapper
gsap.set(buttonWrap, { autoAlpha: 1 });
// Hide the button itself
gsap.set(button,{autoAlpha: 0});
ScrollTrigger.create({
trigger: document.body,
start: `top top-=${minimumScrollDistance}%`,
onEnter: () => {
gsap.fromTo(button,{
autoAlpha: 0,
rotate:-65,
scale: 0.4,
},{
autoAlpha: 1,
rotate:0,
scale: 1,
duration: 0.45,
ease:"power4.out"
})
},
onLeaveBack: () => {
gsap.to(button,{
autoAlpha: 0,
rotate:-65,
scale: 0.6,
duration:0.4,
ease:"power4.out"
})
},
});
button.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
});
}
document.addEventListener("DOMContentLoaded", ()=>{
initBackToTop();
});
Step 3: Add custom CSS
Step 2: Add custom CSS
Custom CSS in Webflow
Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:
CSS
/* Hide the back-to-top wrapper on default (to prevent flash on loaded site) */
[data-back-to-top="wrap"]{ opacity: 0;}
/* Show the button in the Webflow environment */
.wf-design-mode [data-back-to-top="wrap"],
.w-editor [data-back-to-top="wrap"]{ opacity: 1; }
.back-top__arrow-row{
transition: transform 0.5s cubic-bezier(.65, 0, 0, 1);
}
.back-top__button{
transition: border-width 0.5s cubic-bezier(.65, 0, 0, 1);
}
/* Keyboard focus state */
.back-top__button:focus-visible{
border-width: 0.6em;
}
.back-top__button:focus-visible .back-top__arrow-row{
transform: translate(0px, -100%);
}
/* Hover styling */
@media (hover: hover) {
.back-top__button:hover{
border-width: 0.6em;
}
.back-top__button:hover .back-top__arrow-row{
transform: translate(0px, -100%);
}
}
Documentation
This structure basically requires 2 elements on your page:
- A fixed wrapper (with
pointer-events: none
) and an attribute ofdata-back-to-top="wrap"
- A button inside (with
pointer-events: auto
) and an attribute ofdata-back-to-top="button"
The fixed wrapper needs a high z-index
to sit on top of everything else. We must ignore the pointer events on the wrapper, otherwise nothing on your page will be clickable.
Change scroll offset
There's a variable called minimumScrollDistance
in the function. This decides how many % of the viewport a user must scroll before the button appears. For example, if you want it to appear only after scrolling for a full viewport (100vh
) change the value to 100.
Scroll options
In our JS above we use a VanillaJS window.scrollTo
method. Below is 2 alternatives in case you're using Lenis or Locomotive scroll. You'll only have to replace the click listener on the button from our main code snippet into the below versions:
Lenis
Explore more options for this method in the official Lenis documentation.
button.addEventListener('click', () => {
lenis.scrollTo(0, { lerp: 0.1 });
});
Locomotive Scroll
Explore more options for this method in the official Locomotive V5 documentation.
button.addEventListener('click', () => {
locomotiveScroll.scrollTo(0, { lerp: 0.1 });
});
Resource Details
Last updated
August 5, 2025
Type
The Vault
Category
Utilities & Scripts
Need help?
Join Slack