
Page Transitions in Next.js with Motion: Step-by-Step Tutorial
- Author: Md. Saad
- Published at: July 06, 2025
- Updated at: July 08, 2025
Want to add smooth page transitions in your Next.js app without heavy code? You're not alone. Many developers seek ways to create seamless animations in React and Next.js — and Motion (formerly Framer Motion) is one of the most powerful libraries for this purpose. Whether you're building a landing page, portfolio, or single-page application, animated scroll effects and page load transitions can significantly enhance user engagement and retention. In this tutorial, we’ll walk you through how to add fade-ins, scroll-based animations, slide effects, and more using Next.js + Motion. Ready to make your site feel fast, modern, and dynamic?
Getting Started with Motion in Next.js
What is Motion?
Motion is a modern animation library that works with both plain JavaScript and React. If you've used Framer Motion before, you'll find Motion quite familiar. Motion is essentially an evolution of Framer Motion with more flexibility.
Installation
To get started, first scaffold a Next.js app. Then, install the Motion library:
yarn add motion
# or
npm install motion
Project Setup
Start by creating a new Next.js project:
npx create-next-app@latest next-motion
Open your project in VS Code and install the motion package.
Basic Example: Rotating a Square
Step 1: Create a Component
Under the app directory, create a folder components/animate and a file named Rotate.jsx:
'use client';
import { motion } from 'motion/react';
export default function Rotate() {
return (
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 2 }}
className="w-[200px] h-[200px] bg-blue-200 border-2 border-blue-500 rounded-xl flex items-center justify-center" >
<h2 className="text-xl font-semibold">Glad you are here!</h2>
</motion.div>
);
}
Step 2: Use It in a Server Component
In your app/page.js, import and use the component:
import Rotate from './components/animate/Rotate';
export default function Home() {
return <Rotate />;
}
Since motion animations require a browser environment, we need to convert it as cliemt component. So, Rotate.jsx is marked as a client component.
Navbar: Animate on Page Load
Let’s build a fixed navigation bar that smoothly slides down and fades in when the page first loads. This immediate animation helps the interface feel alive and intentional, giving users a visual cue that the site is active and responsive. To achieve this, we can use Motion's initial, animate, and transition properties. The navbar starts with an opacity of 0 and a Y-offset of -60px, and animates to full opacity and its natural Y-position. The transition uses a spring type for a slightly bouncy, friendly motion. You can also customize the animation settings—like the delay, stiffness, damping, or type—to better match your site's personality and tone.
Navbar.js uses initial, animate, and transition.
'use client'
import { motion } from 'motion/react'
export default function Navbar() {
return (
<motion.nav
initial={{ y: -60, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ type: 'spring', stiffness: 60 }}
className="p-4 bg-blue-700 text-white flex gap-6 justify-center sticky top-0 z-50"
>
<a href="#about">About</a>
<a href="#services">Services</a>
<a href="#effects">Effects</a>
<a href="#contact">Contact</a>
</motion.nav>
)
}
Motion Parameters:
- initial → offscreen and transparent
This sets the starting state of the navbar before the animation begins. It starts 60px above its normal position (y: -60) and is fully transparent (opacity: 0), so it’s hidden from view initially. - animate → slides into view
This defines the final state of the navbar once the animation completes. It moves to its natural position (y: 0) and becomes fully visible (opacity: 1), creating the effect of sliding down and fading in. - spring → creates a natural bounce
This controls how the transition happens. Using a spring animation makes the movement feel more lifelike and responsive, with a slight bounce that adds friendliness and polish to the motion.
Scroll-Fade Sections: About, Services, Contact
In the next section, we'll create reusable pieces that animate into view when the user scrolls down the website. This highlights critical portions and keeps the experience interactive. The effect is modest yet captivating: each part starts slightly below its final location with no opacity. When the portion scrolls into view (20% threshold), it animates upward and fades in. We're utilizing the useInView hook to detect scroll entry and useAnimation to regulate timing, together with Motion's initial, animate, and transition properties to ensure seamless transitions. You can also customize the animation behavior—such as the offset, duration, delay, or easing—to match your content’s tone and pacing.
'use client'
import { motion, useAnimation } from 'motion/react'
import { useInView } from 'react-intersection-observer'
import { useEffect } from 'react'
export default function AnimatedSection({ children, id }) {
const controls = useAnimation()
const [ref, inView] = useInView({ triggerOnce: true, threshold: 0.2 })
useEffect(() => {
if (inView) controls.start({ opacity: 1, y: 0 })
}, [inView, controls])
return (
<motion.section
id={id}
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={controls}
transition={{ duration: 0.6 }}
className="p-10 my-20 max-w-4xl mx-auto bg-gray-100 rounded-xl shadow"
>
{children}
</motion.section>
)
}
Motion Parameters:
- initial: { opacity: 0, y: 50 } → starts hidden and pushed down
The element begins fully transparent and slightly below its final position, making it invisible and offset when the page loads. - animate: { opacity: 1, y: 0 } → fades in and slides up
As the animation is triggered, the element fades into view and slides upward into its natural position, creating a clean entrance. - Triggered only when inView using useEffect
The animation only plays when the element enters the viewport. This is managed with the useInView hook and useEffect, ensuring animations respond to user scroll.
You can customize the vertical offset (y), opacity levels, trigger threshold, animation timing, or delay to fine-tune how and when your elements appear on scroll.
Sliding Section (Left-to-Right Scroll Entry)
Here, we're animating a text block to slide in from the left edge of the screen as it becomes visible in the viewport. This is especially useful for drawing attention to content that introduces a new section or provides important information. We implement this using Motion’s initial and whileInView props — starting the element completely off-screen to the left (x: -100vw) and animating it to its default position (x: 0) when scrolled into view. The transition is set to tween, giving us a smooth, linear animation without any spring bounce.
'use client'
import { motion } from 'motion/react'
export default function SlidingSection() {
return (
<motion.div
initial={{ x: '-100vw' }}
whileInView={{ x: 0 }}
transition={{ type: 'tween', duration: 0.8 }}
className="bg-white p-6 shadow rounded-xl mb-8"
>
<h3 className="text-2xl font-semibold mb-2">Sliding Animation</h3>
<p>This section slides in from the left when it enters the viewport.</p>
</motion.div>
)
}
Motion Parameters:
- initial: { x: '-100vw' } → offscreen left
This sets the element's starting position completely off the screen to the left. It won’t be visible until the animation starts. - whileInView: { x: 0 } → slides into place
When the element enters the viewport, it animates to its natural horizontal position (x: 0), effectively sliding in from the left. - transition: 'tween' → smooth linear animation
This defines how the animation progresses. Using tweencreates a consistent, smooth motion without any bounce—ideal for subtle or precise movements.
Scaling Cards (Staggered Scroll Animations)
Now we're going to introduce a visually appealing set of cards that scale up and fade in with a delay between them. This animation pattern is ideal for feature or service lists when you want each item to feel uniquely animated while remaining part of a group. We draw many cards with the map() method and apply motion to each one using initial, whileInView, and transition. Every card animates to full size and full opacity after beginning small and transparent (scale: 0.5, opacity: 0). The card's index (index * 0.2) determines the delay, producing a lovely staggered effect. Additionally, you can alter the starting scale, opacity, delay factor, and transition type to fit the rhythm of your brand and the degree of emphasis you desire for every card.
'use client'
import { motion } from 'motion/react'
export default function ScalingCards() {
const items = ['Design', 'Code', 'Deploy']
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{items.map((item, index) => (
<motion.div
key={item}
initial={{ scale: 0.5, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5, delay: index * 0.2 }}
className="bg-blue-100 p-6 rounded-xl shadow text-center font-semibold text-lg"
>
{item}
</motion.div>
))}
</div>
)
}
Motion Parameters:
- scale: 0.5 → 1 – zooms in
The element starts at half its size (scale: 0.5) and smoothly scales up to its full size (scale: 1), creating a zoom-in effect. - opacity: 0 → 1 – fades in
The element begins fully transparent (opacity: 0) and becomes fully visible (opacity: 1) as it animates, adding a smooth fade-in effect. - delay: index * 0.2 – staggered animation
Each element’s animation is delayed slightly based on its index in a list. This creates a cascading or staggered entrance, giving a more natural and dynamic appearance.
You can customize the scale range, fade speed, delay factor, or even combine it with movement (like y or x) to fine-tune the entrance effect to your desired style.
Combined Animation: Scale + Rotate + Fade
In order to produce a more intricate and striking effect, this section shows how to combine various motion types. In a single fluid motion, the animated block fades in, rotates into place, and scales up. If you want your feature highlights, call-to-actions, or testimonials to stand out, this type of effect is perfect. We establish an initial state with no opacity, a slightly negative rotation (-5 degrees), and a smaller scale (0.8). It rotates to zero, scales to one, and fades in to opacity one as it enters the viewport. A spring transition with a moderate amount of stiffness gives the finished product a polished bounce. Additionally, you can alter every element of the animation to better fit your needs, such as the starting scale, rotation angle, opacity, bounce intensity, or delay.
'use client'
import { motion } from 'motion/react'
export default function CombinedEffect() {
return (
<motion.div
initial={{ scale: 0.8, rotate: -5, opacity: 0 }}
whileInView={{ scale: 1, rotate: 0, opacity: 1 }}
transition={{ duration: 0.8, type: 'spring', stiffness: 60 }}
className="p-10 bg-yellow-100 rounded-xl shadow-md text-center"
>
<h3 className="text-2xl font-bold">Combined Animation</h3>
<p>This element scales, rotates, and fades into view.</p>
</motion.div>
)
}
Motion Parameters:
- scale: 0.8 → 1 – grows in size
The element starts slightly smaller than its final size (scale: 0.8) and gently scales up to full size (scale: 1), creating a smooth grow-in effect. - rotate: -5° → 0° – tilts into place
The element begins with a slight counterclockwise tilt (-5°) and rotates to a natural upright position (0°), adding a subtle, playful movement. - opacity: 0 → 1 – fades in
Starts fully transparent and fades into view, making the entrance feel soft and less abrupt. - spring with stiffness – bouncy and natural feel
The animation uses a spring transition, where stiffness controls how tight and snappy the movement feels. Lower stiffness means a softer bounce, while higher values make it more energetic.
You can customize the scale range, tilt angle, spring stiffness, or combine with other effects (like x/y movement or delay) to match your brand’s motion style and interaction tone.
Bringing It All Together
In this final step, you’re putting everything together to build your complete animated single-page site. The goal here is to arrange all the individual components—your animated navbar, header, scroll-fade sections, sliding content, scaling cards, and combined animation block—into one cohesive flow that the user can smoothly scroll through. This structure allows you to showcase different animation techniques in action while keeping your page clean, readable, and performance-friendly.
You’ll do this by importing all your components into app/page.js and arranging them in a logical order: the navbar sits at the top, followed by the animated header, then each animated section flows naturally as the user scrolls. By assigning unique id values to each section like #about, #services, and #contact, your sticky navbar becomes fully functional with scroll navigation.
With just a few lines of motion configuration and modular React components, you've now created a polished, fully animated landing page that feels interactive, intentional, and professional. You can also customize each section’s animation style—whether it's direction, timing, easing, or trigger point—to align with your brand’s personality and ensure the page flows exactly how you want.
// app/page.js
import * as motion from "motion/react-client";
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import AnimatedSection from '@/components/AnimatedSection'
import SlidingSection from '@/components/SlidingSection'
import ScalingCards from '@/components/ScalingCards'
import CombinedEffect from '@/components/CombinedEffect'
export default function HomePage() {
return (
<>
<Navbar />
<motion.header
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8 }}
className="h-[70vh] flex items-center justify-center bg-gradient-to-br from-blue-500 to-indigo-600 text-white text-center"
>
<div>
<h1 className="text-5xl font-bold">Welcome to Our Animated Page</h1>
<p className="mt-4 text-lg">Scroll down to experience motion</p>
</div>
</motion.header>
<AnimatedSection id="about">
<h2 className="text-3xl font-bold mb-4">About Us</h2>
<p>We build modern websites with clean code and smooth transitions.</p>
</AnimatedSection>
<AnimatedSection id="services">
<h2 className="text-3xl font-bold mb-4">Our Services</h2>
<ul className="list-disc list-inside space-y-2">
<li>Frontend Engineering</li>
<li>UI Animation</li>
<li>Next.js Development</li>
</ul>
</AnimatedSection>
<AnimatedSection id="effects">
<h2 className="text-3xl font-bold mb-8">Motion Effects Gallery</h2>
<SlidingSection />
<ScalingCards />
<CombinedEffect />
</AnimatedSection>
<AnimatedSection id="contact">
<h2 className="text-3xl font-bold mb-4">Contact Us</h2>
<p>Email us at <a href="mailto:hello@example.com" className="text-blue-600 underline">hello@example.com</a></p>
<img src="https://source.unsplash.com/random/800x300" alt="Contact" className="rounded-lg mt-4" />
</AnimatedSection>
<Footer />
</>
)
}
Note: Using motion directly in a server component will throw an error. You can fix this using motion/react-client, which allows server components to utilize motion animations.
Updated Import for Server Component
Change the import like this:
import * as motion from "motion/react-client";
Now you can remove 'use client' and use Motion directly inside server components.
Final Thoughts
Bringing your Next.js app to life with scroll-triggered animations and smooth page transitions doesn’t have to be complex. With the Motion library, you can add fade-ins, slides, scaling, and custom animation combos—all without writing raw CSS or managing external scripts. These effects don’t just look great—they improve user engagement and make your site feel polished and professional. Whether you're building a landing page, a portfolio, or a product site, integrating Next.js with Motion is a modern best practice for developers who care about user experience and performance.
Looking to take your frontend to the next level? Animation isn’t just a design trend, it’s a conversion booster.
Why Work with StaticMania?
At StaticMania, we help teams and businesses craft high-performing, interactive user interfaces with Next.js and Motion. Whether you're launching a fully animated marketing page or transitioning an existing React app to use scroll-triggered interactions, our developers bring the technical and creative precision needed to execute it right. We specialise in:
✔ Smooth page transition architecture
✔ Scroll-based and view-based animation logic
✔ Performance-optimised motion design
✔ Tailored Motion.js setups for single-page apps and landing pages
Need help with Next.js animation or Motion integration? Let's consult with the expert developer to scale the Next.js app.
FAQs
To add animations, install the Motion library and wrap your components using motion elements like motion.div. You can define how elements enter or behave using simple properties like initial, animate, and transition.
Yes, you can. Motion works natively in client components. For server components, a special import allows safe rendering without hydration issues, letting you keep animation logic where you need it.
Use a combination of scroll detection and motion animations to reveal sections when they enter the viewport. This is perfect for building interactive, scroll-friendly landing pages and keeps users engaged.
Staggered animations are great for things like feature cards or service lists. You can apply delays to each item so they animate one after the other, giving your content a more dynamic and polished feel.
Yes. One of Motion's strengths is combining effects easily. You can animate multiple properties in sync—like scaling up while fading in and rotating into view—to create eye-catching transitions.
In many cases, yes. Motion offers more flexibility, better integration with React's lifecycle, and smoother handling of complex UI interactions, like scroll, route transitions, and dynamic layouts.