Get a quote
Performance & SEO

Astro static site performance: what prerendering buys, and where it stops

Every rebuild gets the same advice: switch frameworks. The real question comes first — is the output identical for everyone who asks? One answer prerenders the page, the other renders it per request.

Algoriel 14 min read
Astro static site performance: what prerendering buys, and where it stops
In this article
  1. Where Astro’s speed actually comes from
  2. This site runs on Astro; client systems do not
  3. Does the page have to know who is asking
  4. The line runs between surfaces
  5. Content that changes is not the same as a page that is dynamic
  6. When the condition breaks, speed comes off the read path
  7. Four questions that place a surface
  8. Where Astro is the right answer

Astro prerenders a static site: a page’s HTML is written at build time, before any request arrives, so every visitor receives the same file. Whether that holds is settled surface by surface rather than company by company; one half of a product can be pre-written while the other is produced at request time. On Vipkurum the domain a request arrives on decides which content comes back, so no screen there can be pre-written.

Anyone planning a rebuild hears the same advice: switch frameworks and the site gets faster. Framework choice is the last step of that decision. The first step is narrower — is the output identical for everyone who asks? If it is, the page can be written during the build, and nothing is left to assemble when a visitor arrives. If it is not, the compiler cannot know who will open the page, and somebody has to produce the answer at the moment of the request.

Where Astro’s speed actually comes from

A component in Astro is compiled to HTML on the server while the site is being built, and it stays HTML. No framework runtime reaches the browser unless something asks for it, and asking is explicit: a component runs in the browser only when it carries a client directive. Hydration is opt-in, one component at a time.

That inversion changes what page weight tracks. It follows the number of components someone deliberately marked, not the number of components on the page. On most pages here, nothing is marked.

The second decision concerns the shape of the output. This site’s configuration pins output to static, so every page is written to a file on disk during the build, before any request exists. When a visitor arrives, nothing is assembled; a file is served. The alternative was running a template on every request, and that only pays for itself when the page has to read the request. No page here does.

Third, the framework boundary is drawn by hand. Preact on this site is scoped to a single route: the one carrying the quote form. That is where the only client directive in the source tree lives. Nothing outside the form pulls a Preact bundle, because nothing outside the form has to remember what a visitor typed two steps ago.

None of this makes the site JavaScript-free. The theme guard that has to run before first paint, the navigation drawer, the mega menu, the section reveals and the cookie banner are hand-written and inline, one job each. What ships is a handful of behaviour scripts, and none of them carries a framework runtime or an application shell.

This site runs on Astro; client systems do not

The site you are reading is built with Astro, TypeScript and Preact. Those three appear in no client system we have delivered, and we do not sell Astro as a service either.

Client work runs on one stack: Laravel, MySQL and Redis, with Inertia and Tailwind at the interface. Every phone app that leaves this desk is React Native (Expo) and talks to the same Laravel application the office panel reads. We do not pick a new stack per project; we pick one stack that can be maintained for years. A specialist runtime enters only when the problem forces it, such as Python and OpenCV for image processing.

The split falls out of the fact that the two kinds of surface answer different questions. On Vipkurum, which tenant’s content should come back is settled by the domain the request arrives on. In AlgoKlima, a service record lands in a queue on a technician’s phone, and when it was made and by whom is decided at runtime. Build time has no answer to either question; moving Astro there would only relocate the problem.

Those questions are never asked on this site. A service page reads the same for everyone; so does a case study, and so does this article. Both language trees carry a URL prefix and both are pre-rendered the same way. The output is fixed, so it can be written ahead of time. Had there been a session, a basket or a personal record on the same site, none of those sentences would hold.

Does the page have to know who is asking

Pre-rendering has exactly one condition: the output is the same for everyone who asks. The moment it breaks, the build has nothing to offer, because a compiler cannot know who will open the page. Three situations break the condition in three places.

The first is multi-tenancy. Vipkurum carries 7 institutions on a single core, each under its own domain and brand. Which timetable, which student record and which payment row come back is decided by the request. Writing one institution’s screen ahead of time would mean putting the other institutions’ records into the same build, and the boundary that keeps each institution’s data separate does not allow it.

The second is where the record is first written. In AlgoKlima’s field app, a technician opens a job in a boiler room with no signal. The record goes into a local queue on the phone first. When the connection returns, the queue drains in creation order, and a change made in the field merges with a change made in the office into one record through conflict resolution. The ordering rule itself is runtime information.

The third is the type of the request. At Diotek, a survey appointment and a service call never travel through the same form. A survey centres on a place; a service call centres on an installed unit. The request type splits the flow at the very start, and which flow opens depends on what the visitor came for. The branching behind that form cannot be chosen without reading the request.

In none of the three did a framework debate open. The question was who produces the answer at the moment of the request. When a Laravel application already answers that, a second framework adds maintenance without adding speed.

The line runs between surfaces

The decision is made surface by surface, and both answers can sit inside one product without contradiction.

At Çatalca Merkez Kasap, a printed menu was replaced by a menu page opened from a QR code. Everyone at the table sees the same page: prices do not vary by visitor, and there is no ordering, payment or stock layer behind it. The menu is split into sections and each section has its own address, so codes on different tables land at different points of the same page and moving between sections opens no new page request.

QRVizit, which we are building right now, carries both answers in one product. The page a printed QR code opens shows the same content to everyone who scans it. That content is edited from the owner’s panel, and the panel has to know who signed in. Half the product can be written ahead and the other half cannot, so one product ends up holding two rendering decisions side by side.

Vipkurum sits at the far end of the same spectrum. What comes back depends on the domain the request arrived on and on the role of the person signed in: a teacher, a parent and an administrator read the same record with different permissions. There is not a single screen there that could be written in advance.

SurfaceWhat changes the outputRendering decisionEvidence
QR menu — one page whose sections carry their own addressesNothing about the visitor; the price list is the same for everyone at the table, with no ordering, payment or stock layer behind itCan be written ahead: section moves cost no page request, and a price change is a page update rather than a reprintÇatalca Merkez Kasap
QR business card — public card page plus the owner’s panel (in development)The card page is the same for everyone who scans; the panel has to know who signed inTwo answers in one product: the card page is served on request out of a Redis cache, the panel on request under all conditionsQRVizit
Multi-tenant institution panel — timetables, students, paymentsThe domain the request arrived on, and the role of the person signed inRequest time only: pre-writing it would mean pre-writing every record of every tenantVipkurum
Forecast, archive and climate summary pagesNot the visitor; the calendarProduced by the Laravel application, with every date–location breakdown published at its own permanent addressGünlük Hava Durumu

Read the table left to right: the second column gives the answer and the third column is its consequence. No row is settled by a company name, and two surfaces of the same client can sit in different rows.

Content that changes is not the same as a page that is dynamic

One objection comes up every time: if the content keeps changing, the page cannot stay static. It can. Update frequency is not what decides — what decides is who the output changes for. A page refreshed several times a day is still publishing if it looks the same to everyone; a personal screen updated once a month is state.

Günlük Hava Durumu carries three rates of change on one site. A 15 day forecast is refreshed and overwritten. A day that has passed becomes an archive row, written once and never changed again. The climate summary does not arrive from a separate source; it is computed from that archive and renews itself as the archive grows. All three change with the calendar. None of them changes with the reader.

On the same site, every date–location breakdown keeps its own permanent address. The archive deepens the site without the page structure being redesigned. Address permanence is a decision independent of content freshness: once an address is published, its content may be refreshed but its location may not move. Sites that skip this decision watch addresses drift as the archive grows.

Çatalca shows the same distinction at a smaller scale. When a price changes the page is updated, and the same QR code opens the current menu without a reprint. The content changed; the output is still identical for everyone at the table. On a printed menu the cost of that change was a new print run.

The rule has a clear edge. If the content changes with records the visitor produced rather than with a calendar, there is state and not publishing, and this section stops applying. A screen showing someone their own record cannot be pre-written, however rarely it is updated.

When the condition breaks, speed comes off the read path

When the condition breaks, speed is not abandoned. It is taken from somewhere else. On a page produced at request time, the gain comes from making a repeated read cheap, not from moving rendering earlier.

On the inherited SsangYong corporate site, catalog reads are answered from a Redis cache. The same query does not fall through to MySQL on every page view; the database steps in when data changes. The page is still produced at request time, but the expensive part of producing it is not repeated for each visitor.

In the same engagement, vehicle images were moved into a single naming and folder scheme. Which model uses which file is now visible without reading code. The scattered images had been carrying much of the page weight, and that weight came down without the framework being touched at all.

We also stayed inside the existing framework. The Laravel, Tailwind and jQuery set was kept, working sections were left alone, and the only thing that went in was a cache on the read path. The reason was ownership: the brand’s own team would carry the maintenance, and a dependency that changed the framework would have taken the site out of its owner’s hands. On an inherited system the deciding question is who keeps it running after we leave.

We run the same pattern on a product of our own. In QRVizit we cache the content of the visitor-facing page in Redis, so repeated scans of the same code do not each reach MySQL, while the records themselves stay in MySQL. Laravel, MySQL and Redis are the first thing on the table in system architecture work, and that is where a surface goes when the condition breaks.

Four questions that place a surface

Four questions are enough to place a surface, and the order matters: the first one makes the rest meaningful.

Does the output change depending on who is looking? If it does, the surface cannot be pre-rendered and the discussion ends there. At Vipkurum the request decides which institution’s content comes back, and no framework choice alters that.

Does the content change on a publishing calendar or in a data feed? These are separate questions. On Günlük Hava Durumu the forecast refreshes with the calendar. In AlgoKlima a record appears because a technician moved through a site visit. The first is publishing that can be written ahead; the second is state from the beginning. One site can hold both, and then each screen gets its own answer.

How many years will this surface live? A campaign page that lasts a season and a corporate storefront meant to stand for years do not deserve the same decision. For the second one we pick a stack that whoever inherits it will recognise.

Who lives with the maintenance? We do not only describe upkeep to clients from a distance. We have 5 products of our own, 3 of them under construction right now, and we carry the maintenance of the ones that are live. AlgoKlima is one of them, and we watch what founding decisions such as the offline queue return over years on our own systems.

The four answers are read together. The first puts the surface in one of two camps; the other three say how long it will hold there. When a surface lands on the pre-rendered side but struggles on the last two questions, the rendering decision was right and the plan for who keeps it running is what is missing.

Where Astro is the right answer

Astro is the right answer for this site because the condition holds, not because of the speed. No page here changes its output based on who opened it. Service pages, case studies and this article publish content; none of them carries state. Wherever the condition holds, pre-rendering wins: a corporate storefront, documentation, a campaign page, a blog.

When the condition breaks we do not go looking for another framework. A surface that has to know who is asking moves onto the stack client systems already run on. What separates each institution’s data on Vipkurum is the data model itself; even if that boundary could be moved into build time, it should not be.

This article is its own example. The text was written to HTML during a build, and the file you received is the file everyone else receives. Apply the same rule to an institution panel and the work stops at the sign-in screen.

Neither approach ranks above the other; the surface is what asks the question. Applying one framework to every surface, and rewriting a working system in the name of speed, are both answers given before the question was heard.

The rule fits in a sentence. If the output is the same for everyone, write the page ahead of time; if it is not, produce it when the request arrives. In our web development work we redraw that line at the start of every project, and it is drawn before any framework is named.

Key takeaways

  • The only condition for pre-rendering is that the output is identical for everyone who asks; speed is the consequence of that condition, not the reason for it.
  • Algoriel uses Astro only on its own marketing site; client systems run on Laravel, MySQL and Redis.
  • The static-versus-request-time decision is made per surface: in QRVizit, which Algoriel is building right now, the card page can be pre-written while the panel cannot.
  • Frequent content updates do not make a page dynamic; what matters is whether the output changes with the calendar or with the visitor.
  • When the condition breaks, the surface moves onto Laravel, MySQL and Redis, and the gain then comes from making a repeated read cheap, such as answering catalog reads from a Redis cache.

Questions this article answers

  • If Astro ships no JavaScript, how do the menu and animations on this site work?

    Astro ships no framework runtime by default; components are compiled to HTML at build time, and running a component in the browser requires an explicit client directive. The site is not JavaScript-free, though: the theme guard, navigation drawer, mega menu, section animations and cookie banner are small hand-written behavior scripts rather than a framework runtime. On Algoriel's own site, the only client directive sits on the route that carries the quote form, where Preact loads.

  • Why can't a page be pre-rendered when its output depends on the visitor?

    Pre-rendering writes the page before any request arrives, which only works when the output is the same for everyone who asks. On Vipkurum, which tenant's content should be returned becomes known only from the domain the request arrives on; pre-writing those pages would mean pre-writing every record of every tenant, the opposite of tenant isolation. In AlgoKlima, a service record is written into the technician's phone queue — build time has no answer to that.

  • Can half of a product be static and the other half rendered on request?

    Yes — the decision belongs to the surface, not the product. In QRVizit, which we are building right now, the two sit side by side: the public card page shows the same content to everyone who scans the code, so it can be written ahead of time; the management panel has to know who is signed in, so it is rendered on request by the Laravel application. One product, two surfaces, two rendering decisions.

  • Can a page whose content changes often still be static?

    It can — how often content changes is not what decides. The deciding question is what changes the output. Günlük Hava Durumu changes with the calendar, not the visitor: a forecast is refreshed and overwritten, a day that passes turns into an archive row that never changes again, and the climate summary is computed from that archive. Every date–location breakdown keeps its own permanent address; address permanence is a decision independent of content freshness.

  • Where does speed come from on an inherited site when you can't change the framework?

    On an inherited site, speed comes from the read path and the asset layout, not the framework. On the SsangYong corporate site, catalog reads are answered from a Redis cache and MySQL steps in only when data changes. Vehicle images were moved into a single naming and folder scheme — page weight often comes from scattered assets. The Laravel, Tailwind and jQuery set stayed untouched because the brand's team carries the maintenance.

About the author

Algoriel

Remote web software agency

We build custom web and mobile software for businesses. Here we answer the questions that come up while your software is being built and after it goes live.

Tags:
  • astro
  • performance
  • prerendering
  • static site
  • web development

Have a system to build?

If there is a system you want built, the quote form is the place to describe it.