I'm stuck with a weird ScrollTrigger issue in my Next.js app and could really use some help.
What's happening
I have a section on my homepage that uses GSAP ScrollTrigger to pin and animate when you scroll. Here's the weird part:
When I first visit the homepage (or refresh the page), everything works great. The section pins and animates perfectly.
But when I navigate to another page and then come back to the homepage, the ScrollTrigger just stops working. The section doesn't pin anymore and the animations don't trigger.
The really confusing part
This only happens in production! On localhost, it works fine even when I navigate back and forth between pages. But on my live server, it breaks every time I return to the homepage.
My code
'use client';
import React, { useRef } from 'react';
import { usePathname } from 'next/navigation';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
gsap.registerPlugin(ScrollTrigger);
const ProcessSection = () => {
const sectionRef = useRef(null);
const pathname = usePathname();
useGSAP(() => {
if (pathname !== '/') return;
const el = sectionRef.current;
if (!el) return;
const q = gsap.utils.selector(el);
const isMobile = window.innerWidth < 768;
const scrollDistance = isMobile ? 1500 : 2000;
const tl = gsap.timeline()
.to({}, { duration: 0.4 })
// Slide 1 -> 2
.to(q('.slide-0'), { top: '100%', duration: 0.25, ease: 'power2.inOut' })
.to(q('.slide-1'), { top: '0%', duration: 0.25, ease: 'power2.inOut' }, '<')
.to({}, { duration: 0.4 })
// Slide 2 -> 3
.to(q('.slide-1'), { top: '100%', duration: 0.25, ease: 'power2.inOut' })
.to(q('.slide-2'), { top: '0%', duration: 0.25, ease: 'power2.inOut' }, '<')
.to({}, { duration: 0.4 });
ScrollTrigger.create({
id: 'process-section-pin',
trigger: el,
start: 'top top',
end: `+=${scrollDistance}`,
scrub: 0.5,
pin: true,
pinSpacing: true,
pinReparent: true,
animation: tl,
anticipatePin: 1,
invalidateOnRefresh: true,
});
const onResize = () => {
ScrollTrigger.refresh();
};
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
};
},
{
scope: sectionRef,
dependencies: [pathname],
revertOnUpdate: true,
}
);
return (
<section
ref={sectionRef}
className="bg-secondaryBg py-[clamp(100px,calc(100px+60*(100vw-760px)/1120),160px)] relative"
>
<div className="max-w-[clamp(680px,calc(680px+920*(100vw-760px)/1120),1600px)] px-[15px] mx-auto">
<div className="max-w-[clamp(500px,calc(500px+400*(100vw-760px)/1120),900px)] mx-auto text-center">
<h2 className="md:text-[clamp(48px,calc(48px+30*(100vw-760px)/1120),78px)] text-[clamp(32px,calc(32px+16*(100vw-360px)/400),48px)] md:leading-[clamp(48px,calc(48px+30*(100vw-760px)/1120),78px)] leading-[clamp(32px,calc(32px+16*(100vw-360px)/400),48px)] font-medium">
How we push startup{' '}
<span className="font-bold">ideas into real growth</span>
</h2>
</div>
<div
className="flex flex-col mt-[clamp(20px,calc(20px+40*(100vw-760px)/1120),60px)] mb-[clamp(30px,calc(30px+50*(100vw-760px)/1120),80px)] md:h-[clamp(280px,calc(280px+200*(100vw-760px)/1120),480px)] h-[420px] relative overflow-hidden"
style={{ perspective: '1000px' }}
>
{/* Slide 1 */}
<div
className="slide slide-0 grid md:grid-cols-3 gap-4 absolute left-0 w-full"
style={{ top: '0%' }}
>
<div className="flex flex-col justify-center md:items-start max-md:order-2">
<h4 className="md:text-[clamp(16px,calc(16px+16*(100vw-760px)/1120),32px)] md:leading-[clamp(20px,calc(20px+20*(100vw-760px)/1120),40px)] text-[clamp(26px,calc(26px+10*(100vw-360px)/400),36px)] leading-[clamp(30px,calc(30px+30*(100vw-360px)/400),40px)] font-medium md:max-w-[clamp(160px,calc(160px+200*(100vw-760px)/1120),360px)] max-md:text-center">
Lorem ipsum dolor sit amet.
</h4>
</div>
<div className="flex flex-col justify-center text-center max-md:order-1">
<h3 className="md:text-[clamp(280px,calc(280px+200*(100vw-760px)/1120),480px)] text-[180px] leading-none font-medium">
1
</h3>
</div>
<div className="flex flex-col justify-center md:text-right md:items-end max-md:order-3">
<h4 className="md:text-[clamp(12px,calc(12px+12*(100vw-760px)/1120),24px)] text-[clamp(18px,calc(18px+6*(100vw-360px)/400),24px)] font-medium md:max-w-[clamp(210px,calc(210px+200*(100vw-760px)/1120),410px)] max-md:text-center">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. mollitiia eos ducimus labore. Ab, provident repellat.
</h4>
</div>
</div>
{/* Slide 2 */}
<div
className="slide slide-1 grid md:grid-cols-3 gap-4 absolute left-0 w-full"
style={{ top: '100%' }}
>
<div className="flex flex-col justify-center md:items-start max-md:order-2">
<h4 className="md:text-[clamp(16px,calc(16px+16*(100vw-760px)/1120),32px)] md:leading-[clamp(20px,calc(20px+20*(100vw-760px)/1120),40px)] text-[clamp(26px,calc(26px+10*(100vw-360px)/400),36px)] leading-[clamp(30px,calc(30px+30*(100vw-360px)/400),40px)] font-medium md:max-w-[clamp(160px,calc(160px+200*(100vw-760px)/1120),360px)] max-md:text-center">
Lorem ipsum dolor sit amet.
</h4>
</div>
<div className="flex flex-col justify-center text-center max-md:order-1">
<h3 className="md:text-[clamp(280px,calc(280px+200*(100vw-760px)/1120),480px)] text-[180px] leading-none font-medium">
2
</h3>
</div>
<div className="flex flex-col justify-center md:text-right md:items-end max-md:order-3">
<h4 className="md:text-[clamp(12px,calc(12px+12*(100vw-760px)/1120),24px)] text-[clamp(18px,calc(18px+6*(100vw-360px)/400),24px)] font-medium md:max-w-[clamp(210px,calc(210px+200*(100vw-760px)/1120),410px)] max-md:text-center">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. mollitiia eos ducimus labore. Ab, provident repellat.
</h4>
</div>
</div>
{/* Slide 3 */}
<div
className="slide slide-2 grid md:grid-cols-3 gap-4 absolute left-0 w-full"
style={{ top: '100%' }}
>
<div className="flex flex-col justify-center md:items-start max-md:order-2">
<h4 className="md:text-[clamp(16px,calc(16px+16*(100vw-760px)/1120),32px)] md:leading-[clamp(20px,calc(20px+20*(100vw-760px)/1120),40px)] text-[clamp(26px,calc(26px+10*(100vw-360px)/400),36px)] leading-[clamp(30px,calc(30px+30*(100vw-360px)/400),40px)] font-medium md:max-w-[clamp(160px,calc(160px+200*(100vw-760px)/1120),360px)] max-md:text-center">
Lorem ipsum dolor sit amet.
</h4>
</div>
<div className="flex flex-col justify-center text-center max-md:order-1">
<h3 className="md:text-[clamp(280px,calc(280px+200*(100vw-760px)/1120),480px)] text-[180px] leading-none font-medium">
3
</h3>
</div>
<div className="flex flex-col justify-center md:text-right md:items-end max-md:order-3">
<h4 className="md:text-[clamp(12px,calc(12px+12*(100vw-760px)/1120),24px)] text-[clamp(18px,calc(18px+6*(100vw-360px)/400),24px)] font-medium md:max-w-[clamp(210px,calc(210px+200*(100vw-760px)/1120),410px)] max-md:text-center">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. mollitiia eos ducimus labore. Ab, provident repellat.
</h4>
</div>
</div>
</div>
</div>
</section>
);
};
export default ProcessSection;