NextJS High LPC (Large Content Paint)
Hey, I'm dealing with a problem on my NextJS site where my LPC score is between 3-4 seconds. I've gotten it down to around ~2 seconds by removing google analytics, posthog analytics, client components, and sentry now I'm adding things back to see what the problem is.
Source code: https://github.com/answeroverflow/answeroverflow
Testing method is removing everything from the site, and then building and starting a production verison locally
LCP Improvements List:
- Removing tremor safelist reduced css bundle by 50%
- Using <a> instead of next/link took .3s off of LPC
- Lazy loading avatar images, kept LPC at 2.2
LCP Deprovements List:
- GA 2.2->2.5
- Google Fonts 2.2->2.7
GitHub
GitHub - AnswerOverflow/AnswerOverflow: Indexing Discord Help Chann...
Indexing Discord Help Channel Questions into Google - GitHub - AnswerOverflow/AnswerOverflow: Indexing Discord Help Channel Questions into Google
103 Replies
(not really looking for support btw just for clarifying, this is mainly a thread for me to add things back to see what makes it take longer and i want it indexed but if have suggestions i'll take them )
Empty page, 1.7-1.9 seconds
Result page, recommendations only no client components
I'd expect this to have an LPC that's the same as empty page
Removing the links brings the LPC down, weird
No
<Link/>
vs <Link>
cc @Josh do you use next/link?
I do indeed
this is crazy, it adds like .3 seconds onto my LCP - that can't be right can it?
This is on Next 14 so as up to date as possible
I mean it is technically Js
I'm not using prefetching so I don't see much reason to have it, may evaluate adding it back in the future
Will probably revaluate it since in a realworld test it may not affect it
adding server invite to the page, adds .3s to LPC due to radix use client
Using next/link let's it do router based navigation instead of full page reloads, so you definitely still want it
Somehow in dev mode I got a LCP of 1.4, not sure how exactly
Interesting
I might keep it off of high use pages that people don't navigate off of, i.e the result pages
w/ server components i can send <a> vs <link> depending on if prefetch is passed
0 JS icons w/ adding back server invite keeps LPC below 2 - i need to figure out how to do a 0 JS fallback image incase they error
even with 0 js server invite + icon it's still gone up to 2.1s, this is still with no client components
had a left over client component, just one client component is adding .3s to LPC
that may just be an overall cost to hit with adding client components however, not an exponential growth
Added everything back, got an LPC of 1.9
2.2
Pretty consistently 1.9-2.2, not much impact bringing back the full page which is good
Adding search bar client component, no perf impact - staying between 1.8 and 2.2
Added back footer, no impact to LPC staying between 1.8-2.2
added back discord avatars since i forgot them, slight jump in LCP to 2.3-2.4 max
It's a server component though, and these are loading w/ eager and fetch prority high
added DNS prefetch to cdn.discordapp.com, doesn't impact LPC much
we're also in the danger range now for LPC
Going to try adding the navbar back next, I'm expecting that to impact LPC quite a lot
LCP up to 2.7 with the navbar
blank page ~2-2.1
shadcn navbar is nice but i dont really need it i can just make the same with raw html
0 js navbar, 2.5s LPC
i dont get it
taking the page content away and we're back down to 1.7
might be taking a long time to load the discord icons?
Bringing page back puts it at 2.4, not sure what element is causing that
Problem element seems to be server invite, removing that improves LPC
these are client components with a tracking event for analytics
maybe i can lazy load them so they don't block
yes server invite increases LPC
For some reason when it loads from the initial load instead of the cached load LPC is always a lot lower
Removing client component and server icon improves LPC by .1-.2s
literally only server icon is 2.5s for LPC
Removing server icon brings it down consistently to 2.2s ish
still high, itd be nice to get it closer to the empty page at 1.8 for more buffer room
Removing the avatar icon brings it down to 1.9-2.0 ish so image loading is definetly an issue
Not sure how to improve that, tried eager loading & lazy loading, already preconnceting to discords' cdn
2-2.2s LPC when adding the analytics loader back to the server invite
tried eager loading emojis, made LPC worse
As an aside, I think it'd be worth creating a quick project in Astro to see if loading the same amount of JS is faster
ðŸ˜
im struggling 😦
this really seems like it shouldnt be this impactful, all i want to load is a bit of javascript and some images
why is this so difficult
Setting prority low and lazy loading on avatar images appears to have helped with LPC, sitting at around 2.2 at the moment
Pretty consistently getting 2.2 LPC now with lazy loading avatar images, that seesm to be a key improvement
current baseline: 2.2 LPC
Added the searchbar client component back, still at 2.2
That gives enough of a buffer to bring the rest of the navbar back
Gonna do a quick test adding google analytics back and seeing what happens
Added Google analytics
Run 1 - 2.1
Run 2 - 2.5
Run 3 - 2.5
Run 4 - 2.5
That's a concerning .3 jump - real world experience may be different on that one though
Quick test with next/third-parties
Run 1 - 2.4
Run 2 - 2.5
Run 3 - 2.5
Not really any different
This is with preconnect as well
test
Even Clyde is concerned about my LPC score
Adding Google Fonts
LPC jumps up to 2.7 which is really concerning
here's the font config, i should go through and make sure im only importing weights i use
reverting shows that yes it makes a difference
going to try importing one weight of one font and going from there
adding one font of one weight raises LPC to ~2.4
Two fonts, one weight each brings LPC to 2.6s
Data Unlocker
2-2.4s
Dang, yeah that's adding around .2s to LCP
That one is a really valuable script to have though, not sure how to get around that
Data Unlocker is inconsisent, between 2.0 and 2.4 - I'll keep it for now and move the baseline to 2.4
New Baseline 2.4
Time to test out adding posthog back, i know that one is gonna be rough
im lazy loading it though so maybe just maybe it wont increase lpc
PostHog
No impact on perf 🙌 staying between 2.2-2.5 - bundle size did get 49 kb unused added to it but again, lazy loading thats fine
Interestingly, the blank page is staying at 1.8 for LCP
Next-Themes
Testing out adding Next themes now, unsure how this will impact perf but if it has a major impact i can make a basic version that doesn't wrap the app in use client
Next themes stays around 2.4 so in baseline
Time to add back the rest of the navbar
Header
Adding back the rest of the header brings blank page up to 2.1
real page is up to 3.1
😦
my gut feeling is next is bundling imports that are used in server components files even if they aren't sent to the client
which would be bad
Treemap with unused imports vs with unused imports deleted
LCP without sign in button is up to 2.6
LCP with navbar with sign in button, 2.4-2.6
Very consistently /empty is between 1.9s and 2s to load
So that means there's .5s that can be saved on /m/[id]
Current State:
LPC 2.6
Missing:
Required:
- Signed in dropdown
- Tremor Tailwind Config
- twMerge
Optional:
- Google fonts
- Google analytics
- Next/Link
- error state for avatar
I don't think it is, I actually just had a case where I was importing something with side effects to being imported, and it only got triggered when I referenced the import in the site
Here's where I'm importing it - changed it to a dynamic import and it's still loading really weird
even with dynamic moved after the if statement it still loads the js
just a Q, with a fast FCP (0.9s) and plenty of content on the page already as seen on the snapshots, is that LCP a problem? The page isn't blocked in between, is it. It's just that a piece of it is loading slower
It’s a problem because Google sees the high LCP and then sometimes doesn’t show my site in search results for mobile users - if that wasn’t a factor I wouldn’t really be worried about it
But there’s like a noticeable drop in impressions on mobile so I gotta fix it
I was going to say
oh so this might suck for partial prerender, because until all the suspended stuff gets streamed into the document, it's counting that as LCP, so on simple POC for partial prerender with some delays added to the fetches on a couple of components I'm seeing all orange on LCPbut I just did something that fixed it and I got 0.3s FCP and 0.7s LCP, so never mind I guess
What confuses me is how LCP is calculated, since in the screenshots the largest content area is painted immediately
I’m wondering if it’s next hydrating the page with react that’s causing it to think the html changed when in reality nothing changed
yeah idk myself, I was just getting 5s+ LCP because I have a 3s artificial delay on suspended component and it was seeing that pop in as LCP, but I fixed a bunch of layout shift issues and it's now down 0.6s - either chrome is being funky or idk
it was my assumption that it was hydration or the suspense landing from the stream, but I was wrong
it was seeing a huge ass carousel that was popping in as the LCP element, but after I fixed layout shift between a placeholder and the actual carousel (the one with delayed suspense) it now reports a hero image as the LCP element
so maybe look in the report because it spells out what the LCP element is @Rhys as reece
could be something stupid like my layout shift problem
in this case it's the CTA button at the bottom
in this one it's the message content
so looks like it's not one big thing that contributes to the LCP, but rather bunch of small things that get painted at once?
Yeah, it's not like a large image
It could be a large image if there's an attachment
I mean it's not about how large it is, but if what it highlights actually contributes anything major
https://www.answeroverflow.com/m/1167312424563658773 For this thread, LPC is also the content
NextJS High LPC (Large Content Paint) - Theo's Typesafe Cult
Hey, I'm dealing with a problem on my NextJS site where my LPC score is between 3-4 seconds. I've gotten it down to around ~2 seconds by removing google analytics, posthog analytics, client components, and sentry now I'm adding things back to see what the problem is.
Source code: https://github.com/answeroverflow/answeroverflow
Testing method ...
Which is good at least, the content is the most relevant part
if it highlights an element that on its own is a second then you know where to look, if it shows 20 different things each time and they have small amount of ms to them, then well
this is interesting looking at the tree map, as the number of elements on the page grows the number of script tags nextjs has to add to hydrate the page grows
i figure thats probably having a large impact, but that's just how next works so i cant exactly solve that
Maybe instead of loading all thread messages I just load like the first 10 and load more on scroll
that's how reddit does it
@Rhys as reece how do yo use lighthouse on pages that are protected by auth (must be authenticated to view)
i don't since i don't care about those pages since it doesn't affect my website score with google
Baseline: 4s LCP
Knowing what I know now, I'm going to start from the beginning at try to get LPC down to 3 from now
Removing tremor from css file - 3.8-3.9 LCP
Old CSS file info:
34.9 KiB 300 ms
New CSS file info:
11.4 KiB 150 ms
Removing tw-merge from client components - LCP 3.8 -> 3.4-3.7 LCP hard to tell
Mainly stays around 3.9 LCP,
Bundle size:
Before: 846.4 KiB
After: 821.2 KiB
It's decently possible to enforce these are only used on server, but there may be easier optimizations else where (also what in the world is causing 820 kb of JS)
Using <a> instead of next/link - LCP 3.8 -> LCP 3.4-3.7
Bundle Size:
Before: 846.4 KiB
After: 823.2 KiB
I know exactly what is happened to jump the LCP timing, next/link is used inside of the LCP content, that requires JS, it doesn't run for a while, and then it runs - may be time to ditch next link
Removing client component from avatar images - TODO LCP
Lazy loading avatars - Insignificant
Removing useless navbar JS - 3.4-3.8 -> 3.1 consistently
Before: 823.2 KiB
After: 680.6 KiB
This is gonna suck to improve
Only importing used font weights - TODO LCP
Prority fetching emojis: LCP 3.1 -> 2.9-3.2Hmm isn’t the point of a good score to give users a good experience? Or is there some other reason I’m not aware of
hi @Rhys as reece if you want take a look at these videos, they help quite a lot on understanding and optimizing LCP 😀
https://www.youtube.com/watch?v=fWoI9DXmpdk
https://www.youtube.com/watch?v=AQqFZ5t8uNc
Chrome for Developers
YouTube
A deep dive into optimizing LCP
There is no shortage of advice on the web about improving page load performance, and yet, of the three Core Web Vitals, Largest Contentful Paint (LCP) is still the hardest metric for most sites to consistently meet the recommended "good" threshold. This talk looks at what makes LCP a hard metric to optimize, and offers some concrete strategies t...
Chrome for Developers
YouTube
Optimize for Core Web Vitals
In this hands-on talk, we cover tips & tricks for optimizing your user-experience to meet the Core Web Vitals. We use tools like Lighthouse & DevTools, show you code snippets for fixes, and highlight how you too can get fast and stay fast.
Optimizing for quality of user experience is key to the long-term success of any site on the web. Whether ...