The Problem: Your Page Loads Everything at Once
Open your SaaS landing page on a phone. Watch what happens.
The browser downloads every image, loads every animation, and executes every line of JavaScript — even for sections the user will never scroll to. The hero section competes with the FAQ section for bandwidth. Your above-the-fold content waits in line behind below-the-fold content that nobody has asked for yet.
This is why your mobile Lighthouse score is 45.
The fix is conceptually simple: only load what the user can see. Everything else waits until they scroll to it.
This is lazy loading, and when implemented correctly, it cuts load time by 40-60%.
What Lazy Loading Actually Means
Lazy loading is a strategy where you defer the loading or rendering of content until the user needs it. Instead of loading a 3MB page all at once, you load a 500KB page (just the hero) and add the rest as the user scrolls.
There are three types of lazy loading that matter for SaaS pages:
1. Image Lazy Loading
Images below the fold do not download until the user scrolls near them. The browser shows a placeholder (or nothing) and swaps in the real image when it enters the viewport.
2. Component Lazy Loading
Heavy JavaScript components (interactive demos, charts, 3D elements, video players) do not load their code until needed. This keeps initial JavaScript small and fast.
3. Section Lazy Loading
Entire page sections do not render until the user scrolls. The DOM stays small on initial load, reducing rendering time and preventing layout shifts.
I use all three on every SaaS project I build. Here is how each one works.
Image Lazy Loading: The Easy Win
This is the simplest optimization and has the biggest impact for image-heavy pages.
How It Works
The browser has a native loading="lazy" attribute:
<img src="feature-screenshot.webp" loading="lazy" alt="Dashboard feature" />
When this attribute is present, the browser only downloads the image when the user scrolls within a certain distance of it (typically 1250px on desktop, 2500px on mobile).
In Next.js
The Next.js Image component lazy loads by default:
import Image from 'next/image';
// This lazy loads automatically
<Image
src="/feature-screenshot.webp"
alt="Dashboard feature showing real-time analytics"
width={800}
height={600}
/>
// Only the hero image should eager-load
<Image
src="/hero-image.webp"
alt="SaaS product hero"
width={1200}
height={800}
priority // This disables lazy loading
/>
Key rule: Only one or two images should have priority — your hero image and maybe your logo. Everything else lazy loads.
Impact
On a typical SaaS landing page with 8-12 images, lazy loading reduces initial page weight by 40-60%. A page that downloads 2.5MB of images on first load now downloads 400KB.
Component Lazy Loading: Taming Heavy JavaScript
Some SaaS landing pages include interactive elements that require large JavaScript libraries:
- 3D visualizations (Three.js: ~600KB)
- Charts and graphs (Chart.js: ~200KB, Recharts: ~300KB)
- Code editors (Monaco: ~2MB)
- Video players (custom players with controls)
- Maps (Mapbox/Google Maps: ~200KB)
- Animation libraries (GSAP: ~100KB, Framer Motion: ~150KB)
Loading all of these on page load means the browser downloads and parses megabytes of JavaScript before the user sees anything.
Dynamic Imports in Next.js
import dynamic from 'next/dynamic';
// This component's code only loads on the client, after hydration
const InteractiveDemo = dynamic(
() => import('@/components/InteractiveDemo'),
{ ssr: false }
);
// This component loads when it is needed
const ChartSection = dynamic(
() => import('@/components/ChartSection'),
);
The ssr: false option is critical for components that use browser APIs (canvas, WebGL, window). It tells Next.js: "do not try to render this on the server."
Impact
Dynamic imports reduce initial JavaScript bundle size by 30-50%. A page that ships 1.2MB of JavaScript on first load now ships 600KB. The rest loads on demand.
Section Lazy Loading: The Nuclear Option
This is the most aggressive optimization and the one that makes the biggest difference for Lighthouse scores.
The Concept
Instead of rendering all 10 sections of your landing page in the initial HTML, you render only the hero. Everything below it is wrapped in a component that says: "do not exist until the user scrolls."
My Implementation
I use a DeferredContent component that works like this:
export default function LandingPage() {
return (
<>
<Hero />
<DeferredContent>
<Features />
<HowItWorks />
<Pricing />
<Testimonials />
<FAQ />
<CTA />
</DeferredContent>
</>
);
}
How DeferredContent works:
- Server-side: All content renders normally in the HTML (crawlers see everything)
- Client-side: CSS hides the deferred content (
content-visibility: hidden) - On first scroll: The CSS is removed and all content becomes visible
- Fallback: After 3.5 seconds, content shows even without scrolling
This approach is SEO-safe because the full HTML is in the server response. Googlebot sees all your content. Real users just get a faster first paint.
Why This Works So Well
When the browser only needs to render one section instead of ten:
- DOM size is smaller — fewer elements to paint
- JavaScript execution is reduced — components below the fold do not hydrate until visible
- Layout calculations are simpler — the browser does not need to compute positions for 10 sections
- CLS is eliminated — hidden content cannot shift visible content
Impact
Section lazy loading typically improves:
- LCP by 30-50% (less competition for rendering resources)
- TBT by 40-70% (less JavaScript to execute)
- CLS to near zero (hidden content does not participate in layout)
Putting It All Together
Here is the complete lazy loading strategy I use on every SaaS project:
Layer 1: Images
- Hero image:
priority(eager load) - All other images: lazy load (default in Next.js)
- Use modern formats (AVIF/WebP) with proper
sizesattribute
Layer 2: Components
- Animation libraries: loaded via
requestIdleCallback - Interactive components:
dynamic(() => import(...), { ssr: false }) - Analytics scripts:
strategy="lazyOnload"withnext/script
Layer 3: Sections
- Hero: renders immediately
- Everything below hero: wrapped in
DeferredContent - Individual heavy sections: additional
LazySectionwrappers if needed
The Result
| Metric | Without Lazy Loading | With Full Strategy |
|---|---|---|
| LCP | 3.8s | 1.2s |
| TBT | 2,400ms | 90ms |
| CLS | 0.45 | 0.002 |
| Page weight (initial) | 3.2MB | 680KB |
| Lighthouse Performance | 38 | 96 |
That is a 60% reduction in load time and a 2.5x improvement in Lighthouse score — from the same content, same design, same functionality.
Common Mistakes
1. Lazy loading the hero image
Your hero image is the Largest Contentful Paint. Lazy loading it makes LCP worse, not better. Always use priority on hero images.
2. Lazy loading everything with IntersectionObserver
IntersectionObserver is great, but it only fires after JavaScript loads and executes. If your initial JavaScript is heavy, IntersectionObserver-based lazy loading does not help with first paint.
3. Forgetting about SEO
If you use useState(false) to defer rendering, the server HTML does not include the content. Crawlers that do not execute JavaScript (Bing, social media scrapers) miss it. Always ensure deferred content is in the server HTML.
4. Not setting proper dimensions
Lazy loaded images without width and height cause CLS when they pop in. Always define dimensions.
Want This on Your SaaS?
This lazy loading strategy is built into every project I deliver. You get a landing page that loads in under 2 seconds on mobile, scores 90+ on Lighthouse, and does not sacrifice SEO or design quality.
Check your current page speed or get a free audit to see exactly how lazy loading could improve your specific site.
FAQ
Does lazy loading affect SEO?
Not when done correctly. The key is ensuring your server-rendered HTML contains all content. Search engines parse the HTML, not the visual state of the page. My approach renders everything in HTML and hides it visually with CSS — so crawlers see the full page.
Will users notice the lazy loading?
No. Sections load before the user scrolls to them (using scroll detection + viewport margin). Images start downloading when they are 1000-2500px away from the viewport. Everything appears instantly when the user arrives.
Can I add lazy loading to my existing site?
Yes, image lazy loading can be added to any site with the loading="lazy" attribute. Component and section lazy loading require framework support (React, Next.js, Vue). If your site is built on Webflow or WordPress, the options are more limited.