Effect
Scroll Progress Bar Override
This is a demo website showing you how to create a scroll progress bar in Framer that reflects your position on the page. Feel free to remix the file, copy the progress bar into your own site, and follow the set-up instructions listed below.
You can click this link to get the starter file I used in the tutorial video, so you can follow along.
Created by
About the resource
First, there's the progress bar itself, which we'll position at the top of the website with fixed positioning.
Next, there's a frame nested within the progress bar, known as the "line". We're going to animate this "line" from 0% width to 100% width as you scroll down the site with a code override applied to the frame.
Finally, there's an additional "head" frame within the 'line' frame. This acts like a glow at the end of the scroll bar line. We'll position this absolutely and pin it to the right side of the "line".
The code override works by transforming the 'line' frame's width from 0% to 100% between the "start" and "end" scroll sections on your site.
So, you'll need to add a scroll section called "start" where you want the progress bar animation to kick off, and a scroll section called "end" where the scroll bar should reach the right edge of the page.
Just copy and paste the code override from below into your file.
Watch the full video tutorial for more guidance.
Code override for the scroll progress bar line
Copy this code and paste it into a new override in your Framer project. Next, apply it to the 'line' frame that's within the progress bar.
About the resource
First, there's the progress bar itself, which we'll position at the top of the website with fixed positioning.
Next, there's a frame nested within the progress bar, known as the "line". We're going to animate this "line" from 0% width to 100% width as you scroll down the site with a code override applied to the frame.
Finally, there's an additional "head" frame within the 'line' frame. This acts like a glow at the end of the scroll bar line. We'll position this absolutely and pin it to the right side of the "line".
The code override works by transforming the 'line' frame's width from 0% to 100% between the "start" and "end" scroll sections on your site.
So, you'll need to add a scroll section called "start" where you want the progress bar animation to kick off, and a scroll section called "end" where the scroll bar should reach the right edge of the page.
Just copy and paste the code override from below into your file.
Watch the full video tutorial for more guidance.
Code override for the scroll progress bar line
Copy this code and paste it into a new override in your Framer project. Next, apply it to the 'line' frame that's within the progress bar.
About the resource
First, there's the progress bar itself, which we'll position at the top of the website with fixed positioning.
Next, there's a frame nested within the progress bar, known as the "line". We're going to animate this "line" from 0% width to 100% width as you scroll down the site with a code override applied to the frame.
Finally, there's an additional "head" frame within the 'line' frame. This acts like a glow at the end of the scroll bar line. We'll position this absolutely and pin it to the right side of the "line".
The code override works by transforming the 'line' frame's width from 0% to 100% between the "start" and "end" scroll sections on your site.
So, you'll need to add a scroll section called "start" where you want the progress bar animation to kick off, and a scroll section called "end" where the scroll bar should reach the right edge of the page.
Just copy and paste the code override from below into your file.
Watch the full video tutorial for more guidance.
Code override for the scroll progress bar line
Copy this code and paste it into a new override in your Framer project. Next, apply it to the 'line' frame that's within the progress bar.
import type { ComponentType } from "react" import { useState, useEffect, useRef } from "react" export function withProgress(Component): ComponentType { return (props) => { const [progress, setProgress] = useState(0) const [isScrolling, setIsScrolling] = useState(false) const prevScrollY = useRef(0) useEffect(() => { const calculateProgress = () => { const startEl = document.getElementById("start") const endEl = document.getElementById("end") if (startEl && endEl) { const sectionHeight = endEl.offsetTop - startEl.offsetTop const scrollDistance = window.pageYOffset || document.documentElement.scrollTop const sectionScrollDistance = scrollDistance - startEl.offsetTop const sectionProgress = (sectionScrollDistance / sectionHeight) * 100 setProgress(sectionProgress) } } const handleScroll = () => { const scrollY = window.pageYOffset || document.documentElement.scrollTop if (scrollY !== prevScrollY.current) { prevScrollY.current = scrollY calculateProgress() setIsScrolling(true) } } const handleScrollEnd = () => { setIsScrolling(false) } calculateProgress() window.addEventListener("scroll", handleScroll) window.addEventListener("scrollend", handleScrollEnd) return () => { window.removeEventListener("scroll", handleScroll) window.removeEventListener("scrollend", handleScrollEnd) } }, []) return ( <Component {...props} style={{ width: `${progress}%`, transition: isScrolling ? "none" : "width 0.5s ease-in-out", }} /> ) } }
import type { ComponentType } from "react" import { useState, useEffect, useRef } from "react" export function withProgress(Component): ComponentType { return (props) => { const [progress, setProgress] = useState(0) const [isScrolling, setIsScrolling] = useState(false) const prevScrollY = useRef(0) useEffect(() => { const calculateProgress = () => { const startEl = document.getElementById("start") const endEl = document.getElementById("end") if (startEl && endEl) { const sectionHeight = endEl.offsetTop - startEl.offsetTop const scrollDistance = window.pageYOffset || document.documentElement.scrollTop const sectionScrollDistance = scrollDistance - startEl.offsetTop const sectionProgress = (sectionScrollDistance / sectionHeight) * 100 setProgress(sectionProgress) } } const handleScroll = () => { const scrollY = window.pageYOffset || document.documentElement.scrollTop if (scrollY !== prevScrollY.current) { prevScrollY.current = scrollY calculateProgress() setIsScrolling(true) } } const handleScrollEnd = () => { setIsScrolling(false) } calculateProgress() window.addEventListener("scroll", handleScroll) window.addEventListener("scrollend", handleScrollEnd) return () => { window.removeEventListener("scroll", handleScroll) window.removeEventListener("scrollend", handleScrollEnd) } }, []) return ( <Component {...props} style={{ width: `${progress}%`, transition: isScrolling ? "none" : "width 0.5s ease-in-out", }} /> ) } }
import type { ComponentType } from "react" import { useState, useEffect, useRef } from "react" export function withProgress(Component): ComponentType { return (props) => { const [progress, setProgress] = useState(0) const [isScrolling, setIsScrolling] = useState(false) const prevScrollY = useRef(0) useEffect(() => { const calculateProgress = () => { const startEl = document.getElementById("start") const endEl = document.getElementById("end") if (startEl && endEl) { const sectionHeight = endEl.offsetTop - startEl.offsetTop const scrollDistance = window.pageYOffset || document.documentElement.scrollTop const sectionScrollDistance = scrollDistance - startEl.offsetTop const sectionProgress = (sectionScrollDistance / sectionHeight) * 100 setProgress(sectionProgress) } } const handleScroll = () => { const scrollY = window.pageYOffset || document.documentElement.scrollTop if (scrollY !== prevScrollY.current) { prevScrollY.current = scrollY calculateProgress() setIsScrolling(true) } } const handleScrollEnd = () => { setIsScrolling(false) } calculateProgress() window.addEventListener("scroll", handleScroll) window.addEventListener("scrollend", handleScrollEnd) return () => { window.removeEventListener("scroll", handleScroll) window.removeEventListener("scrollend", handleScrollEnd) } }, []) return ( <Component {...props} style={{ width: `${progress}%`, transition: isScrolling ? "none" : "width 0.5s ease-in-out", }} /> ) } }