Skip to main content
RapidDev - Software Development Agency
lovable-issues

Fixing Anchor Link Scroll Issues in Lovable Single-Page Apps

Hash links like href="#features" do not scroll to the target section in Lovable apps because React Router intercepts the navigation instead of letting the browser handle the hash. Fix this by using scrollIntoView in an onClick handler: document.getElementById('features')?.scrollIntoView({ behavior: 'smooth' }). For hash links that need to work across different pages, install the react-router-hash-link package and replace Link with HashLink. Add scroll-mt-16 to target elements to offset fixed headers.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read~5 minAll Lovable versions (React Router v6 with BrowserRouter)March 2026RapidDev Engineering Team
TL;DR

Hash links like href="#features" do not scroll to the target section in Lovable apps because React Router intercepts the navigation instead of letting the browser handle the hash. Fix this by using scrollIntoView in an onClick handler: document.getElementById('features')?.scrollIntoView({ behavior: 'smooth' }). For hash links that need to work across different pages, install the react-router-hash-link package and replace Link with HashLink. Add scroll-mt-16 to target elements to offset fixed headers.

Why anchor links do not scroll in Lovable single-page apps

In a traditional multi-page website, clicking a link like <a href="#features"> makes the browser scroll to the element with id="features". This is native browser behavior that works without any JavaScript. However, Lovable apps use React Router with BrowserRouter, which intercepts all navigation events. When React Router intercepts a hash link click, it updates the URL hash but does not trigger the browser's native scroll-to-anchor behavior. The URL changes to include #features, but the page stays at its current scroll position. This is a well-known limitation of client-side routing in SPAs. The problem is compounded when navigating to a hash link from a different page. If you click a link to /about#team from the home page, React Router navigates to /about but does not scroll to the #team section. The page loads at the top, and the user has to scroll down manually to find the team section. This is especially frustrating for landing pages that rely heavily on anchor navigation.

  • React Router intercepts hash link clicks and does not trigger native browser scroll-to-anchor behavior
  • The Link component from react-router-dom does not handle hash fragments for scrolling
  • Target elements missing an id attribute that matches the hash in the URL
  • Fixed headers or navigation bars covering the scroll target because scroll-margin-top is not set
  • Smooth scrolling CSS (scroll-behavior: smooth) not applied to the html element

Error messages you might see

Clicking anchor link changes URL hash but page does not scroll

This is the expected behavior in React Router SPAs. The URL updates but no scroll happens. You need to manually trigger scrollIntoView when the hash link is clicked.

Hash link scrolls to the element but it is hidden behind the fixed header

The browser scrolls the element to the very top of the viewport, but your fixed navigation bar covers it. Add scroll-margin-top (Tailwind: scroll-mt-16 or scroll-mt-20) to the target element to offset the header height.

Before you start

  • A Lovable project with sections you want to scroll to (e.g., a landing page with Features, Pricing, FAQ sections)
  • Each target section has a unique id attribute (e.g., id="features")
  • The project uses React Router (standard for Lovable projects)

How to fix it

1

Use scrollIntoView for same-page anchor links

scrollIntoView is the simplest approach for hash links within the same page

Instead of using href="#features" on an anchor tag or Link component, add an onClick handler that finds the target element by ID and scrolls to it. Use scrollIntoView with behavior: 'smooth' for an animated scroll. This bypasses React Router entirely and uses the browser's native scroll API. This approach works for navigation menus, table of contents, and back-to-top links.

Before
typescript
// Hash link — React Router intercepts it, no scrolling happens
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
<a href="#faq">FAQ</a>
After
typescript
// onClick with scrollIntoView — smooth scroll to each section
<button
onClick={() => document.getElementById("features")?.scrollIntoView({ behavior: "smooth" })}
className="hover:underline"
>
Features
</button>
<button
onClick={() => document.getElementById("pricing")?.scrollIntoView({ behavior: "smooth" })}
className="hover:underline"
>
Pricing
</button>
<button
onClick={() => document.getElementById("faq")?.scrollIntoView({ behavior: "smooth" })}
className="hover:underline"
>
FAQ
</button>

Expected result: Clicking each button smoothly scrolls to the corresponding section. The URL does not change, avoiding React Router interference.

2

Add scroll-margin-top to offset fixed headers

Without a scroll margin, the target element scrolls behind your fixed navigation bar

If your Lovable app has a fixed header (position: fixed or sticky), the scroll target will be hidden behind it. Add Tailwind's scroll-mt class to each target section to offset the header height. Measure your header height and use the matching Tailwind class: scroll-mt-16 for a 4rem header, scroll-mt-20 for a 5rem header. Add the id attribute to the same element.

Before
typescript
<!-- Section scrolls behind the fixed 64px header -->
<section id="features">
<h2>Features</h2>
<p>Our amazing features...</p>
</section>
After
typescript
<!-- scroll-mt-16 adds 64px offset so the section is visible below the header -->
<section id="features" className="scroll-mt-16">
<h2 className="text-3xl font-bold">Features</h2>
<p>Our amazing features...</p>
</section>

Expected result: Scrolling to #features positions the section heading just below the fixed header, fully visible to the user.

3

Create a reusable ScrollLink component

A reusable component avoids repeating the scrollIntoView logic across your entire app

Create a component that accepts a target section ID and renders a styled clickable element with the scroll behavior built in. This component can be used in your navigation, footer, sidebar, or anywhere you need anchor links. It handles the smooth scroll and optional URL hash update.

Before
typescript
// Repeating scrollIntoView logic everywhere
<button onClick={() => document.getElementById("features")?.scrollIntoView({ behavior: "smooth" })}>
Features
</button>
// Copy-pasted for every anchor link...
After
typescript
// Using the reusable ScrollLink component
import ScrollLink from "@/components/ScrollLink";
<ScrollLink targetId="features">Features</ScrollLink>
<ScrollLink targetId="pricing">Pricing</ScrollLink>
<ScrollLink targetId="faq">FAQ</ScrollLink>

Expected result: All anchor links in the app use the same consistent scroll behavior. Adding new scroll links requires just one line of code.

4

Handle cross-page hash links with react-router-hash-link

When navigating from /about#team, the page must first load /about and then scroll to #team

For hash links that navigate to a section on a different page, the built-in scrollIntoView approach does not work because the target element does not exist yet on the current page. Install react-router-hash-link by asking Lovable: 'Add the react-router-hash-link npm package to the project.' Then replace Link components with HashLink for cross-page anchor navigation. If integrating this library across your generated navigation requires changes in multiple components, RapidDev's engineers have handled this pattern across 600+ Lovable projects.

Before
typescript
// Link component — navigates to /about but ignores #team
import { Link } from "react-router-dom";
<Link to="/about#team">Meet the Team</Link>
// Page loads at /about but stays at the top, does not scroll to #team
After
typescript
// HashLink — navigates to /about and scrolls to #team
import { HashLink } from "react-router-hash-link";
<HashLink smooth to="/about#team">Meet the Team</HashLink>
// Page loads at /about and smoothly scrolls to the #team section

Expected result: Clicking the link navigates to /about and automatically scrolls to the element with id='team' after the page renders.

Complete code example

src/components/ScrollLink.tsx
1import { cn } from "@/lib/utils";
2
3interface ScrollLinkProps {
4 targetId: string;
5 children: React.ReactNode;
6 className?: string;
7 offset?: number; // Extra offset in pixels for fixed headers
8}
9
10// Reusable scroll-to-section link component
11// Bypasses React Router for same-page anchor navigation
12const ScrollLink = ({ targetId, children, className, offset = 0 }: ScrollLinkProps) => {
13 const handleClick = (e: React.MouseEvent) => {
14 e.preventDefault();
15 const element = document.getElementById(targetId);
16 if (!element) return;
17
18 // Calculate position accounting for fixed header offset
19 const elementPosition = element.getBoundingClientRect().top + window.scrollY;
20 const offsetPosition = elementPosition - offset;
21
22 window.scrollTo({
23 top: offsetPosition,
24 behavior: "smooth",
25 });
26
27 // Update URL hash without triggering React Router
28 window.history.pushState(null, "", `#${targetId}`);
29 };
30
31 return (
32 <button
33 onClick={handleClick}
34 className={cn(
35 "text-foreground hover:text-primary transition-colors cursor-pointer",
36 className
37 )}
38 >
39 {children}
40 </button>
41 );
42};
43
44// Example: Landing page navigation using ScrollLink
45const LandingNav = () => {
46 return (
47 <nav className="fixed top-0 w-full bg-background/80 backdrop-blur z-50 border-b">
48 <div className="container flex items-center justify-between h-16">
49 <span className="text-xl font-bold">MyApp</span>
50 <div className="flex gap-6">
51 <ScrollLink targetId="features" offset={64}>Features</ScrollLink>
52 <ScrollLink targetId="pricing" offset={64}>Pricing</ScrollLink>
53 <ScrollLink targetId="faq" offset={64}>FAQ</ScrollLink>
54 </div>
55 </div>
56 </nav>
57 );
58};
59
60export default ScrollLink;

Best practices to prevent this

  • Use scrollIntoView with behavior: 'smooth' for same-page anchor links — it is simpler and more reliable than React Router hash handling
  • Add scroll-mt-16 (or appropriate size) to target sections when using a fixed header to prevent the section from being hidden
  • Give every scroll target a unique id attribute that matches the hash in the link (id="features" for #features)
  • For cross-page hash links, use react-router-hash-link's HashLink component instead of React Router's Link
  • Create a reusable ScrollLink component to avoid repeating scrollIntoView logic across your app
  • Use window.history.pushState to update the URL hash without triggering React Router navigation
  • Test scroll behavior on mobile devices — touch scrolling and fixed headers behave differently than on desktop

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My Lovable (lovable.dev) app has a landing page with anchor links (#features, #pricing, #faq) that do not scroll to the target sections when clicked. The app uses React Router v6 with BrowserRouter. Here is my current navigation component: [paste your nav code here] And here is the landing page with sections: [paste your landing page code here] Please: 1. Fix the anchor links to scroll smoothly to each section 2. Account for my fixed header height of [X]px 3. Create a reusable ScrollLink component I can use across the site 4. Show me how to handle hash links from other pages (e.g., /about#team)

Lovable Prompt

The anchor links in @src/components/Navbar.tsx do not scroll to the target sections on the landing page. Please replace the hash href links with onClick handlers that use document.getElementById and scrollIntoView with smooth behavior. Add scroll-mt-16 to each target section element in @src/pages/Home.tsx to offset the fixed header. Also create a reusable @src/components/ScrollLink.tsx component that I can use for any anchor link.

Frequently asked questions

Why don't anchor links scroll in my Lovable app?

React Router intercepts navigation events, including hash links, and does not trigger the browser's native scroll-to-anchor behavior. Use scrollIntoView in an onClick handler or the react-router-hash-link package instead of standard hash links.

How do I add smooth scrolling to anchor links in React?

Call document.getElementById('sectionId')?.scrollIntoView({ behavior: 'smooth' }) in an onClick handler. This provides a smooth animated scroll to the target element without any additional libraries.

Why does my section scroll behind the fixed header?

The browser scrolls the element to the top of the viewport, but your fixed header covers that area. Add Tailwind's scroll-mt-16 class (or appropriate size matching your header height) to the target section element to offset the scroll position.

How do I scroll to a section on a different page?

Install the react-router-hash-link package and use its HashLink component with the smooth prop: <HashLink smooth to='/about#team'>Team</HashLink>. This first navigates to /about, then scrolls to the #team section after the page renders.

Can I update the URL hash when scrolling without triggering React Router?

Yes. Use window.history.pushState(null, '', '#sectionId') after scrolling. This updates the URL hash for bookmarking and sharing without triggering React Router's navigation system.

Does scroll-behavior: smooth in CSS work with React Router?

Adding scroll-behavior: smooth to the html element enables smooth scrolling for any programmatic scroll, but it does not fix the core issue of React Router intercepting hash links. You still need scrollIntoView or HashLink to trigger the scroll.

What if I can't fix anchor scrolling myself?

If your landing page has complex scroll interactions spanning multiple components and routes, RapidDev's engineers can implement reliable anchor navigation. They have built scroll-based navigation patterns across 600+ Lovable projects.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.