Let's say you have a page with Main section and Footer. By default, rendering of the page looks something like this:
Build time:
- nothing 🤷♂️
Request time:
- Page rendering (in parallel):
- Main:
- Network requests are made
- React renders the component
- Footer:
- Network requests are made
- React renders the component
- Page html travels to the client
To somehow optimize this situation, next provides 2 instruments - data caching and static routes. Let's first explore static route scenario. In this case rendering journey will look like this:
Build time:
- Page rendering (in parallel):
- Main:
- Network requests are made
- React renders the component
- Footer:
- Network requests are made
- React renders the component
Request time:
- Page html travels to the client
That's it, all requests are made build-time as well as rendering corresponding components. But what if we can't do that? E.g. data for Main is user-dependant. And here's PPR comes for the rescue. With it enabled, rendering will look like the mix of two above:
Build time:
- PPR:
- Footer:
- Network requests are made
- React renders the component
Request time:
- Page rendering:
- Main:
- Network requests are made
- React renders the component
- Footer - entire component is taken from cache
- Page html travels to the client
So on the request time we're basically saving on Footer data fetching and rendering. That looks great, let's enable it and observe immediate improvements on TTFB, country GDP and salary compensation:
Build time:
- nothing 🤷♂️
Request time:
- Page rendering (in parallel):
- Main:
- Network requests are made
- React renders the component
- Footer:
- Network requests are made
- React renders the component
- Page html travels to the client
That's not what we expected, the page behaves exactly like default case :thinking_face: The reason for that is when we make requests, we're reading next's dynamic APIs to pass request headers to the monolith. That makes our calls to the monolith uncacheable and make next to bail out from any static optimization. Okay, let's disable PPR for now and address data caching real quick. Let's change request logic for Footer in a way it doesn't use dynamic APIs and cache those requests:
Build time:
- nothing 🤷♂️
Request time:
- Page rendering (in parallel):
- Main:
- Network requests are made
- React renders the component
- Footer:
- Network requests are made once, then taken from cache
- React renders the component
- Page html travels to the client
Cool, that basically saved us time to make requests for Footer! Let's enable PPR again. Now, we observe what we expected from PPR initially:
Build time:
- PPR:
- Footer:
- Network requests are made
- React renders the component
Request time:
- Page rendering:
- Main:
- Network requests are made
- React renders the component
- Footer - entire component is taken from cache
- Page html travels to the client
But comparing with previous situation, we're only saving on Footer rendering, since network requests are already optimized by data caching step.
Also, it's only possible to use PPR if static component doesn't use dynamic APIs at all, but:
- Data fetching method might use it to pass some request headers
- The component itself might use those APIs (we do use them and atm it doesn't seem possible to avoid that)
Bottom line of this story:
- Proper data fetching setup lets you leverage data caching and save on requests made to the backend
- PPR lets you additionally save on component rendering, but to use it you need to address data fetching first