| name | page-scrolling |
| description | Add smooth scrolling for same-page anchor links only. Uses JavaScript to handle |
Page Scrolling
Enable smooth scrolling for same-page anchor links only. Page navigations always start at the top instantly.
Important: Why JavaScript Instead of CSS
CSS scroll-behavior: smooth affects ALL scrolling, including Next.js page navigations. This causes unwanted smooth scrolling when changing pages.
Our approach: Use JavaScript to handle only #anchor link clicks with smooth scroll. Page navigations use browser default (instant to top).
Workflow
- Create PageScrolling Component - JavaScript handler for anchor links
- Add to Layout - Include component in locale layout
- Update globals.css - Keep only
scroll-padding-top, removescroll-behavior - Test - Verify anchor links smooth scroll, page changes are instant
Implementation
Step 1: Create PageScrolling Component
Create website/components/ui/page-scrolling.tsx:
"use client";
import { useEffect } from "react";
export function PageScrolling() {
useEffect(() => {
// Check for reduced motion preference
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
const handleClick = (e: MouseEvent) => {
const target = e.target as HTMLElement;
const anchor = target.closest('a[href^="#"]');
if (anchor) {
const href = anchor.getAttribute("href");
if (href && href.startsWith("#") && href.length > 1) {
const element = document.querySelector(href);
if (element) {
e.preventDefault();
element.scrollIntoView({
behavior: prefersReducedMotion ? "auto" : "smooth",
});
}
}
}
};
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);
return null;
}
Step 2: Add to Layout
Add PageScrolling to app/[locale]/layout.tsx:
import { PageScrolling } from "@/components/ui/page-scrolling";
export default function LocaleLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<PageScrolling />
{/* rest of layout */}
</>
);
}
Step 3: Update globals.css
Ensure globals.css has scroll-padding but NOT scroll-behavior:
@layer base {
html {
/* Smooth scroll handled by JS for anchor links only */
scroll-padding-top: 5rem; /* Offset for fixed navbar */
}
}
DO NOT add scroll-behavior: smooth - this causes page navigation issues.
Same-Page Anchor Links
For links that scroll to sections on the same page, use the # anchor format:
// In navbar or any component
<Link href="#pricing">Pricing</Link>
<Link href="#faq">FAQ</Link>
<Link href="#contact">Contact</Link>
// Target sections need matching IDs
<section id="pricing">...</section>
<section id="faq">...</section>
<section id="contact">...</section>
Adding Section IDs
Each section that should be a scroll target needs an id attribute:
// Before
<section className="py-16 lg:py-24">
// After
<section id="features" className="py-16 lg:py-24">
Common Section IDs
| Section | ID |
|---|---|
| Hero | hero |
| Features | features |
| Pricing | pricing |
| FAQ | faq |
| Contact | contact |
| About | about |
| Portfolio/Projects | projects |
| Testimonials | testimonials |
Scroll Offset for Fixed Navbar
The scroll-padding-top in globals.css handles the offset:
html {
scroll-padding-top: 5rem; /* Adjust based on navbar height */
}
This is respected by scrollIntoView().
Behavior Summary
| Action | Scroll Behavior |
|---|---|
Click #anchor link |
Smooth scroll to section |
| Navigate to new page | Instant to top (no animation) |
| Browser back/forward | Browser default |
| Reduced motion user | Instant scroll |
Checklist
-
PageScrollingcomponent created atcomponents/ui/page-scrolling.tsx - Component added to
app/[locale]/layout.tsx -
scroll-padding-topset in globals.css - NO
scroll-behavior: smoothin globals.css - Section IDs added to scrollable targets
- Test: clicking
#anchorlink scrolls smoothly - Test: navigating to new page is instant (no scroll animation)
- Test: reduced motion preference disables smooth scroll
Output
After running this skill:
components/ui/page-scrolling.tsx- Anchor link handlerapp/[locale]/layout.tsx- Updated with PageScrollingglobals.css- Only scroll-padding-top, no scroll-behavior