Hunting down slow first load bugs in NextJS and Vercel

The first load of a page takes >4.2s, despite the fact that we are rendering heavier stuff on the client and the API responds typically in <700ms (it’s a lot of data). Subsequent loads still hit the API (which caches), but takes are less time to load despite no speed up on the server side.
I can tell this from logging in the getServerSideProps function on Vercel:
notion imagenotion image
Not sure what those 8 and 9ms ones are, but it’s generally quick.
Did a recording in the browser:
notion imagenotion image
WOAH that’s way too long!
Clicking on the LCP (no indication I can click on it, and I can’t click on FCP or DCL, thanks Google) I can see this:
notion imagenotion image
An image is taking that long? What’s that component?
notion imagenotion image
While I’ve never had a good experience with <Image> in NextJS (and optimization is turned off at the project level), I suspected it’s the fallbackSrc so I changed it to a conditional src since .BannerURL only exists if we have it anyway:
notion imagenotion image
Hmm… not sure that was it because if I do a first load on my local host with that new code I see a similar flame graph, with the longest being that banner still (although the visual rendering process does look a bit different now). I am thinking that is a bit of a wild goose chase, but while I wait on staging to deploy I’ll check the flame graph again.
Notably, there is one long part in the Timings section:
notion imagenotion image
Ah great that’s a ton of info to go off of…
Not being an expert, I look to stack overflow to the rescue:
This means that Next.js-before-hydration, by definition, includes:
Alright, it’s time for the tried and true method: commenting shit out until it starts being fast.
I ripped out all the code that calls the getServerSideProps and just have. 5-line file that is <p>hey</p>... Speed returns! e2e in 0.77s, nice!
I tried adding back in the SSR but with no depending components, and the slowness came back…
Alright, time to leave getServerSideProps in, but remove anything that depends on it. The only thing that it does is get the creator info (<1s), and the _app.tsx takes the response and sticks it in a context. At this point I’m either thinking the context is slowing it down, or the bandwidth to pass a few KB around NextJS on the server side is super slow.
Alright, getServerSideProps only (without dependents) is making it slow… time to remove that and see what we get. If it’s fast, next step is to remove the interception of the response in _app.tsx.
And… It’s fast! Alright so let’s remove the propagation to the context, and try again…
It’s getServerSideProps. Not sure if it can handle something this large (although it’s not particularly large), and I know the function gets the response in under 1s so I’m not sure what is turning it into >4s… but getServerSideProps is slow.
Final check is to basically have it do nothing, just run and return a blank object.