Astro, prerendering a single component in a SSR page
By Flavio Copes
Learn how to prerender a single expensive component inside a server-rendered Astro page, using an Astro 3.4 partial with prerender and partial set to true.
To prerender just one part of a server-rendered Astro page, you can extract that part into a page partial (🆕 in Astro 3.4), prerender the partial at build time, and load it from the client.
Here’s the problem I had. One of my server-rendered pages did a quite intensive calculation. The result was the same for everyone, but since the page was SSR, the calculation ran on every single page load. Wasted work, slower responses.
The data only changed when I deployed, so it was a perfect candidate for prerendering. But I didn’t want to prerender the whole page, just that one expensive section.
The partial
I created a new .astro file under src/pages and set two flags in its frontmatter:
---
export const prerender = true
export const partial = true
//the expensive calculation goes here
---
<!-- the HTML for the expensive section -->
prerender = true tells Astro to render this route once at build time, even though the rest of the site runs in SSR mode. The expensive calculation now runs at build, not on every request.
partial = true tells Astro this route is an HTML fragment. Astro skips the <!DOCTYPE html> declaration and doesn’t wrap the output in <html> and <head> tags. That’s what you want for something you’ll inject into an existing page.
Since the file lives in src/pages, it gets its own URL, and the prerendered HTML is served as a static file.
Loading it into the SSR page
Then I included this partial client-side using HTMX:
<div hx-get="/stats-summary" hx-trigger="load" hx-swap="innerHTML"></div>
When the page loads, HTMX fetches the fragment and swaps it into the div.
You could also just use a fetch() request and swap some innerHTML:
const res = await fetch('/stats-summary')
document.querySelector('#stats').innerHTML = await res.text()
Either way, the SSR page stays dynamic, and the heavy part is served as static HTML.
One thing to keep in mind
The partial is frozen at build time. It only updates when you rebuild and redeploy the site.
That’s fine for data that changes rarely, like the stats I was computing. It’s the wrong tool for per-user or per-request data. If I had used it for something user-specific, every visitor would have seen the same stale fragment. For that kind of data, keep the rendering on the server.
Related posts about astro: