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 scrollThis 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 headerThe 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
Use scrollIntoView for same-page anchor links
scrollIntoView is the simplest approach for hash links within the same page
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.
// Hash link — React Router intercepts it, no scrolling happens<a href="#features">Features</a><a href="#pricing">Pricing</a><a href="#faq">FAQ</a>// 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.
Add scroll-margin-top to offset fixed headers
Without a scroll margin, the target element scrolls behind your fixed navigation bar
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.
<!-- Section scrolls behind the fixed 64px header --><section id="features"> <h2>Features</h2> <p>Our amazing features...</p></section><!-- 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.
Create a reusable ScrollLink component
A reusable component avoids repeating the scrollIntoView logic across your entire app
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.
// Repeating scrollIntoView logic everywhere<button onClick={() => document.getElementById("features")?.scrollIntoView({ behavior: "smooth" })}> Features</button>// Copy-pasted for every anchor link...// Using the reusable ScrollLink componentimport 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.
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
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.
// Link component — navigates to /about but ignores #teamimport { 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// HashLink — navigates to /about and scrolls to #teamimport { 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 sectionExpected result: Clicking the link navigates to /about and automatically scrolls to the element with id='team' after the page renders.
Complete code example
1import { cn } from "@/lib/utils";23interface ScrollLinkProps {4 targetId: string;5 children: React.ReactNode;6 className?: string;7 offset?: number; // Extra offset in pixels for fixed headers8}910// Reusable scroll-to-section link component11// Bypasses React Router for same-page anchor navigation12const 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;1718 // Calculate position accounting for fixed header offset19 const elementPosition = element.getBoundingClientRect().top + window.scrollY;20 const offsetPosition = elementPosition - offset;2122 window.scrollTo({23 top: offsetPosition,24 behavior: "smooth",25 });2627 // Update URL hash without triggering React Router28 window.history.pushState(null, "", `#${targetId}`);29 };3031 return (32 <button33 onClick={handleClick}34 className={cn(35 "text-foreground hover:text-primary transition-colors cursor-pointer",36 className37 )}38 >39 {children}40 </button>41 );42};4344// Example: Landing page navigation using ScrollLink45const 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};5960export 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.
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)
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your issue.
Book a free consultation