Improve page loading performance by deferring off-screen content

Implement lazy loading for lower sections of the homepage to optimize initial render and improve user experience.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1f7e7e89-a520-4970-9645-37daadc466dc
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 97dab47f-9107-41f0-9b7a-5900b3b486e6
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/1f7e7e89-a520-4970-9645-37daadc466dc/ZApZ5Qi
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic 2026-03-05 14:36:42 +00:00
parent 970c395184
commit 9a81e63740

View File

@ -15,7 +15,7 @@ import { RecipeWidget } from "@/components/recipe-widget";
import { NewsWidget } from "@/components/news-widget"; import { NewsWidget } from "@/components/news-widget";
import { SidebarWeatherWidget } from "@/components/weather-widget"; import { SidebarWeatherWidget } from "@/components/weather-widget";
import { BreakingNewsWidget } from "@/components/breaking-news-widget"; import { BreakingNewsWidget } from "@/components/breaking-news-widget";
import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useState, useEffect, useCallback, useRef, useMemo, lazy, Suspense } from "react";
function useFocalPoints() { function useFocalPoints() {
const { data } = useQuery<Record<string, { x: number; y: number }>>({ const { data } = useQuery<Record<string, { x: number; y: number }>>({
@ -466,6 +466,33 @@ function FeaturedCarousel({ articles, popular, galleryImages, focalPoints }: { a
); );
} }
function LazySection({ children, minHeight = "200px" }: { children: JSX.Element; minHeight?: string }) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const obs = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
obs.disconnect();
}
},
{ rootMargin: "300px" }
);
obs.observe(el);
return () => obs.disconnect();
}, []);
return (
<div ref={ref} style={{ minHeight: visible ? undefined : minHeight }}>
{visible ? children : null}
</div>
);
}
function BentoSkeleton() { function BentoSkeleton() {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
@ -663,41 +690,47 @@ export default function Home() {
</div> </div>
{gridRows.map((row, ri) => ( {gridRows.map((row, ri) => (
<div key={`row-group-${ri}`}> <LazySection key={`row-group-${ri}`} minHeight="280px">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <div>
{row.map((item) => <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
item.type === "widget" {row.map((item) =>
? <div key={item.key}>{item.widget!.el}</div> item.type === "widget"
: item.type === "ad" ? <div key={item.key}>{item.widget!.el}</div>
? <div key={item.key} className="h-full" data-testid={`ad-grid-${item.key}`}><ArticleCardAd /></div> : item.type === "ad"
: item.article ? <div key={item.key} className="h-full" data-testid={`ad-grid-${item.key}`}><ArticleCardAd /></div>
? <MediumCard key={item.key} article={item.article} focalPoints={focalPoints} /> : item.article
: null ? <MediumCard key={item.key} article={item.article} focalPoints={focalPoints} />
)} : null
{ri === gridRows.length - 1 && widePickedArticles.length > 0 && ( )}
<div className="sm:col-span-2 lg:col-span-4 grid grid-cols-1 sm:grid-cols-2 gap-4"> {ri === gridRows.length - 1 && widePickedArticles.length > 0 && (
<WideCardClassic article={widePickedArticles[0]} focalPoints={focalPoints} /> <div className="sm:col-span-2 lg:col-span-4 grid grid-cols-1 sm:grid-cols-2 gap-4">
{widePickedArticles[1] && <WideCardClassic article={widePickedArticles[1]} focalPoints={focalPoints} />} <WideCardClassic article={widePickedArticles[0]} focalPoints={focalPoints} />
</div> {widePickedArticles[1] && <WideCardClassic article={widePickedArticles[1]} focalPoints={focalPoints} />}
)} </div>
)}
</div>
</div> </div>
</div> </LazySection>
))} ))}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <LazySection minHeight="280px">
{bottomSection.map((item, i) => ( <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div key={`bottom-${i}`}> {bottomSection.map((item, i) => (
{item.el} <div key={`bottom-${i}`}>
</div> {item.el}
))} </div>
</div> ))}
</div>
</LazySection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <LazySection minHeight="280px">
<PhotoGalleryWidget key="extra-gallery" /> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<NewsWidget key="extra-news" /> <PhotoGalleryWidget key="extra-gallery" />
<RecipeWidget key="extra-recipe" /> <NewsWidget key="extra-news" />
<div className="h-full"><ArticleCardAd /></div> <RecipeWidget key="extra-recipe" />
</div> <div className="h-full"><ArticleCardAd /></div>
</div>
</LazySection>
</main> </main>
<Footer /> <Footer />