Creating a Parallax Scroll Image Slider with Framer Motion and Tailwind CSS
June 28, 2025
In this blog post, I'll show you how I built a smooth parallax scroll image slider using Framer Motion, Next.js, and Tailwind CSS. This component creates a beautiful horizontal scroll effect that reacts to vertical scrolling — perfect for galleries, hero sections, or adding subtle depth to any landing page.
Understanding the Component
The component displays two rows of images that move horizontally in opposite directions as the user scrolls vertically. This creates a smooth, layered parallax effect that brings depth to your UI — perfect for galleries, hero sections, or showcasing portfolios.
Setting Up the Environment
We are using React, Framer Motion and Tailwind CSS for styling.
- Tailwind CSS
- React
- Framer Motion
Setting Up the Image Rows
Before we add any motion or scrolling effects, let’s start with the basic layout — two horizontal rows of images stacked vertically:
"use client";
import Image from "next/image";
import React from "react";
const Index = () => {
const leftImages = [
"/ImgParallax/img/g.jpg",
"/ImgParallax/img/gg.jpg",
"/ImgParallax/img/ss.jpg",
"/ImgParallax/img/ssss.jpg",
"/ImgParallax/img/ttt.jpg"
];
const rightImages = [
"/ImgParallax/img/ss.jpg",
"/ImgParallax/img/dd.jpg",
"/ImgParallax/img/ttt.jpg",
"/ImgParallax/img/sss.jpg",
"/ImgParallax/img/g.jpg"
];
return (
<div className="overflow-hidden relative z-10 w-full bg-EerieBlack px-0 sm:px-3 lg:px-5 py-52 md:py-32">
<div className="flex justify-center gap-3 md:gap-4 py-0.5 md:py-2 h-32 md:h-56">
{leftImages.map((src, index) => (
<Image
key={index}
alt={`img-${index}`}
src={src}
width={380}
height={253.36}
className="h-3/4 w-3/4 md:w-full md:h-full"
/>
))}
</div>
<div className="flex justify-center gap-3 md:gap-4 py-0.5 md:py-2 h-32 md:h-56">
{rightImages.map((src, index) => (
<Image
key={index}
alt={`img-${index}`}
src={src}
width={380}
height={253.36}
className="h-3/4 w-3/4 md:w-full md:h-full"
/>
))}
</div>
</div>
);
};
export default Index;
We should have something like this:

Adding Scroll-Based Parallax Motion
With the static image rows set up, it’s time to bring them to life with a smooth parallax effect. In this step, you’ll use Framer Motion’s useScroll and useTransform hooks.
🔍 What does this do?
- Track how much you’ve scrolled through the image container.
- Map that scroll progress to horizontal movement for both rows.
- Wrap the rows in <motion.div> so they animate as you scroll.
"use client";
import { useScroll, useTransform, motion } from "framer-motion";
import Image from "next/image";
import React, { useRef } from "react";
const Index = () => {
const container = useRef(null);
const { scrollYProgress } = useScroll({
target: container,
offset: ["start end", "end start"]
});
const xLeft = useTransform(scrollYProgress, [0, 1], [0, -150]);
const xRight = useTransform(scrollYProgress, [0, 1], [0, 100]);
const leftImages = [
"/ImgParallax/img/g.jpg",
"/ImgParallax/img/gg.jpg",
"/ImgParallax/img/ss.jpg",
"/ImgParallax/img/ssss.jpg",
"/ImgParallax/img/ttt.jpg"
];
const rightImages = [
"/ImgParallax/img/ss.jpg",
"/ImgParallax/img/dd.jpg",
"/ImgParallax/img/ttt.jpg",
"/ImgParallax/img/sss.jpg",
"/ImgParallax/img/g.jpg"
];
return (
<div
ref={container}
className="overflow-hidden relative z-10 w-full bg-EerieBlack px-0 sm:px-3 lg:px-5 py-52 md:py-32"
>
<motion.div
className="flex justify-center gap-3 md:gap-4 py-0.5 md:py-2 h-32 md:h-56"
style={{ x: xLeft }}
>
{leftImages.map((src, index) => (
<Image
key={index}
alt={`img-${index}`}
src={src}
width={380}
height={253.36}
className="h-3/4 w-3/4 md:w-full md:h-full"
/>
))}
</motion.div>
<motion.div
className="flex justify-center gap-3 md:gap-4 py-0.5 md:py-2 h-32 md:h-56"
style={{ x: xRight }}
>
{rightImages.map((src, index) => (
<Image
key={index}
alt={`img-${index}`}
src={src}
width={380}
height={253.36}
className="h-3/4 w-3/4 md:w-full md:h-full"
/>
))}
</motion.div>
</div>
);
};
export default Index;
Attach a ref
Attach a ref to the container. This tells useScroll exactly which section to watch:
const container = useRef(null);
Track scroll progress
- scrollYProgress goes from 0 → 1 as the container enters and leaves the viewport.
- The offset controls when the animation starts and ends.
const { scrollYProgress } = useScroll({
target: container,
offset: ["start end", "end start"]
});
Map scroll progress to horizontal motion
- The left row moves left up to -150px.
- The right row moves right up to +100px.
const xLeft = useTransform(scrollYProgress, [0, 1], [0, -150]);
const xRight = useTransform(scrollYProgress, [0, 1], [0, 100]);
Wrap rows in <motion.div> and bind the x transform using the style prop — this makes the rows slide smoothly as you scroll.
See It In Action
🎉 Conclusion
You've successfully created a smooth Parallax Scroll Image Slider component! This demonstrates how simple scroll-based animations can add depth and interactivity to your Next.js projects with Framer Motion and Tailwind CSS.