Skip to content

Commit 1b49a8e

Browse files
committed
chore: improve lighthouse performance
1 parent 161cdee commit 1b49a8e

3 files changed

Lines changed: 140 additions & 78 deletions

File tree

src/components/LazyMarkdown.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Suspense, lazy } from 'react'
2+
import { useIntersectionObserver } from '~/hooks/useIntersectionObserver'
3+
4+
const Markdown = lazy(() =>
5+
import('./Markdown').then((mod) => ({ default: mod.Markdown })),
6+
)
7+
8+
interface LazyMarkdownProps {
9+
rawContent: string
10+
className?: string
11+
}
12+
13+
export function LazyMarkdown({ rawContent, className }: LazyMarkdownProps) {
14+
const { ref, isIntersecting } = useIntersectionObserver({
15+
rootMargin: '100px',
16+
triggerOnce: true,
17+
})
18+
19+
return (
20+
<div ref={ref} className={className}>
21+
{isIntersecting ? (
22+
<Suspense fallback={null}>
23+
<Markdown rawContent={rawContent} />
24+
</Suspense>
25+
) : null}
26+
</div>
27+
)
28+
}

src/routes/__root.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const Route = createRootRouteWithContext<{
6262
}),
6363
],
6464
links: [
65+
{ rel: 'preload', href: appCss, as: 'style' },
6566
{ rel: 'stylesheet', href: appCss },
6667
{
6768
rel: 'apple-touch-icon',
@@ -91,14 +92,25 @@ export const Route = createRootRouteWithContext<{
9192
},
9293
],
9394
scripts: [
94-
// Google Tag Manager script
95+
// Google Tag Manager - deferred until user interaction or timeout
9596
{
9697
children: `
97-
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
98-
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
99-
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
100-
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
101-
})(window,document,'script','dataLayer','GTM-5N57KQT4');
98+
(function(){
99+
var loaded=false;
100+
function loadGTM(){
101+
if(loaded)return;
102+
loaded=true;
103+
var w=window,d=document,l='dataLayer',i='GTM-5N57KQT4';
104+
w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
105+
var f=d.getElementsByTagName('script')[0],j=d.createElement('script');
106+
j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i;
107+
f.parentNode.insertBefore(j,f);
108+
}
109+
['scroll','click','touchstart','keydown'].forEach(function(e){
110+
window.addEventListener(e,loadGTM,{once:true,passive:true});
111+
});
112+
setTimeout(loadGTM,4000);
113+
})();
102114
`,
103115
},
104116
],

src/routes/_libraries/index.tsx

Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Link, MatchRoute, createFileRoute } from '@tanstack/react-router'
1+
import {
2+
Await,
3+
Link,
4+
MatchRoute,
5+
createFileRoute,
6+
} from '@tanstack/react-router'
27
import { twMerge } from 'tailwind-merge'
38
import { Footer } from '~/components/Footer'
49
import { LazySponsorSection } from '~/components/LazySponsorSection'
@@ -17,7 +22,7 @@ import { coreMaintainers } from '~/libraries/maintainers'
1722
import { useToast } from '~/components/ToastProvider'
1823
import { formatAuthors, getPublishedPosts } from '~/utils/blog'
1924
import { format } from 'date-fns'
20-
import { Markdown } from '~/components/Markdown'
25+
import { LazyMarkdown } from '~/components/LazyMarkdown'
2126
import { createServerFn } from '@tanstack/react-start'
2227
import { setResponseHeaders } from '@tanstack/react-start/server'
2328
import { AdGate } from '~/contexts/AdsContext'
@@ -70,11 +75,11 @@ const fetchRecentPosts = createServerFn({ method: 'GET' }).handler(async () => {
7075

7176
export const Route = createFileRoute('/_libraries/')({
7277
loader: async ({ context: { queryClient } }) => {
73-
await queryClient.ensureQueryData(ossStatsQuery())
74-
const recentPosts = await fetchRecentPosts()
78+
queryClient.ensureQueryData(ossStatsQuery())
79+
const recentPostsPromise = fetchRecentPosts()
7580

7681
return {
77-
recentPosts,
82+
recentPostsPromise,
7883
}
7984
},
8085
component: Index,
@@ -101,7 +106,7 @@ function Index() {
101106
fn: bytesSignupServerFn,
102107
})
103108
const { notify } = useToast()
104-
const { recentPosts } = Route.useLoaderData()
109+
const { recentPostsPromise } = Route.useLoaderData()
105110

106111
// sponsorsPromise no longer needed - using lazy loading
107112

@@ -115,11 +120,13 @@ function Index() {
115120
src={'/images/logos/splash-light.png'}
116121
className="w-[300px] pt-8 xl:pt-0 xl:w-[400px] 2xl:w-[500px] dark:hidden"
117122
alt="TanStack Logo"
123+
fetchPriority="high"
118124
/>
119125
<img
120126
src={'/images/logos/splash-dark.png'}
121127
className="w-[300px] pt-8 xl:pt-0 xl:w-[400px] 2xl:w-[500px] hidden dark:block"
122128
alt="TanStack Logo"
129+
fetchPriority="high"
123130
/>
124131
</BrandContextMenu>
125132
<div className="flex flex-col items-center gap-6 text-center px-4 xl:text-left xl:items-start">
@@ -372,80 +379,95 @@ function Index() {
372379
</div>
373380
</div>
374381

375-
{recentPosts && recentPosts.length > 0 && (
376-
<div className="px-4 lg:max-w-(--breakpoint-lg) md:mx-auto">
377-
<h3 id="blog" className={`text-4xl font-light mb-6 scroll-mt-24`}>
378-
<a
379-
href="#blog"
380-
className="hover:underline decoration-gray-400 dark:decoration-gray-600"
381-
>
382-
Latest Blog Posts
383-
</a>
384-
</h3>
385-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
386-
{recentPosts.map(
387-
({ slug, title, published, excerpt, authors }) => {
388-
return (
389-
<Link
390-
key={slug}
391-
to="/blog/$"
392-
params={{ _splat: slug }}
393-
className={`flex flex-col gap-3 justify-between
382+
<Await promise={recentPostsPromise}>
383+
{(recentPosts: any) => (
384+
<>
385+
{recentPosts && recentPosts.length > 0 && (
386+
<div className="px-4 lg:max-w-(--breakpoint-lg) md:mx-auto">
387+
<h3
388+
id="blog"
389+
className={`text-4xl font-light mb-6 scroll-mt-24`}
390+
>
391+
<a
392+
href="#blog"
393+
className="hover:underline decoration-gray-400 dark:decoration-gray-600"
394+
>
395+
Latest Blog Posts
396+
</a>
397+
</h3>
398+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
399+
{recentPosts.map(
400+
({ slug, title, published, excerpt, authors }) => {
401+
return (
402+
<Link
403+
key={slug}
404+
to="/blog/$"
405+
params={{ _splat: slug }}
406+
className={`flex flex-col gap-3 justify-between
394407
border-2 border-transparent rounded-lg p-4
395408
transition-all bg-white/90 dark:bg-black/40
396409
shadow-md dark:shadow-lg dark:shadow-blue-500/20
397410
hover:border-blue-500 hover:shadow-xl
398411
`}
399-
>
400-
<div>
401-
<div className={`text-base font-bold`}>{title}</div>
402-
<div
403-
className={`text-xs italic font-light mt-1 text-gray-600 dark:text-gray-400`}
404-
>
405-
<p>
406-
by {formatAuthors(authors)}
407-
{published ? (
408-
<time
409-
dateTime={published}
410-
title={format(
411-
new Date(published),
412-
'MMM dd, yyyy',
413-
)}
414-
>
415-
{' '}
416-
on {format(new Date(published), 'MMM dd, yyyy')}
417-
</time>
418-
) : null}
419-
</p>
420-
</div>
421-
{excerpt && (
422-
<div
423-
className={`text-xs mt-3 text-gray-700 dark:text-gray-300 line-clamp-2 leading-relaxed`}
424412
>
425-
<Markdown rawContent={excerpt} />
426-
</div>
427-
)}
428-
</div>
429-
<div>
430-
<div className="text-blue-500 uppercase font-bold text-xs">
431-
Read More →
432-
</div>
433-
</div>
413+
<div>
414+
<div className={`text-base font-bold`}>
415+
{title}
416+
</div>
417+
<div
418+
className={`text-xs italic font-light mt-1 text-gray-600 dark:text-gray-400`}
419+
>
420+
<p>
421+
by {formatAuthors(authors)}
422+
{published ? (
423+
<time
424+
dateTime={published}
425+
title={format(
426+
new Date(published),
427+
'MMM dd, yyyy',
428+
)}
429+
>
430+
{' '}
431+
on{' '}
432+
{format(
433+
new Date(published),
434+
'MMM dd, yyyy',
435+
)}
436+
</time>
437+
) : null}
438+
</p>
439+
</div>
440+
{excerpt && (
441+
<div
442+
className={`text-xs mt-3 text-gray-700 dark:text-gray-300 line-clamp-2 leading-relaxed`}
443+
>
444+
<LazyMarkdown rawContent={excerpt} />
445+
</div>
446+
)}
447+
</div>
448+
<div>
449+
<div className="text-blue-500 uppercase font-bold text-xs">
450+
Read More →
451+
</div>
452+
</div>
453+
</Link>
454+
)
455+
},
456+
)}
457+
</div>
458+
<div className="text-center mt-6">
459+
<Link
460+
to="/blog"
461+
className="inline-flex items-center text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors"
462+
>
463+
View All Posts →
434464
</Link>
435-
)
436-
},
465+
</div>
466+
</div>
437467
)}
438-
</div>
439-
<div className="text-center mt-6">
440-
<Link
441-
to="/blog"
442-
className="inline-flex items-center text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors"
443-
>
444-
View All Posts →
445-
</Link>
446-
</div>
447-
</div>
448-
)}
468+
</>
469+
)}
470+
</Await>
449471

450472
<div className={`lg:max-w-(--breakpoint-lg) px-4 mx-auto`}>
451473
<h3 id="courses" className={`text-4xl font-light mb-6 scroll-mt-24`}>

0 commit comments

Comments
 (0)