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.
| Surface | What changes the output | Rendering decision | Evidence |
|---|---|---|---|
| QR menu — one page whose sections carry their own addresses | Nothing about the visitor; the price list is the same for everyone at the table, with no ordering, payment or stock layer behind it | Can 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 in | Two answers in one product: the card page is served on request out of a Redis cache, the panel on request under all conditions | QRVizit |
| Multi-tenant institution panel — timetables, students, payments | The domain the request arrived on, and the role of the person signed in | Request time only: pre-writing it would mean pre-writing every record of every tenant | Vipkurum |
| Forecast, archive and climate summary pages | Not the visitor; the calendar | Produced by the Laravel application, with every date–location breakdown published at its own permanent address | Gü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.