Frontend Engineering

Astro site engineering

Astro's default is that a page ships as HTML and nothing else. This is the engineering that decision leaves to you: which components are interactive, where their data comes from, and what a crawler actually receives.

The problem

The framework removes the default and leaves the decisions

Most frameworks assume a page is an application and make you opt out of shipping JavaScript. Astro assumes a page is a document and makes you opt in. That inverts the usual failure and introduces a different one: a site that is fast when it is built and gradually stops being fast, because islands are added one at a time by different people for different reasons, each addition reasonable on its own, and nobody ever asked to look at the total.

  • A component is interactive and nobody can say why it needs the browser
  • The site started light and each release added a little JavaScript back
  • Content is fetched in the browser for something that was known at build time
  • A collection schema is loose enough to accept any entry at all
  • An island and the static markup around it disagree about what the page says
  • The build regenerates the whole estate for a change to one entry
  • Third-party scripts are on every page and their cost was never counted
  • Someone has asked whether the site should become a single-page application
  • A rendering mode was chosen once and every route has inherited it since
Who this is for

The people who usually bring us this problem

A team with an Astro site that has grown

It works, the islands accumulated rather than being chosen, and the performance the framework promised is no longer the performance the site has.

A team building on Astro

You want the static-by-default premise kept deliberately as the site grows, rather than rediscovered after a slow release.

A team that needs its pages found in search

The site already serves complete HTML, which is the right start, and the properties a crawler depends on still have to be verified rather than assumed.

What it costs

What this costs while it goes unfixed

Engineering faults are rarely confined to the engineering layer. These are the commercial consequences we see most often.

Static by default is a choice you have to keep making

Astro's build produces HTML with no client JavaScript unless a component asks for it. That is the whole benefit and it is not self-maintaining: each new interaction is a local decision made by whoever is writing that component, and nothing in the framework aggregates them. A site can be static in every individual part and still ship an application's worth of JavaScript, because no one was ever asked to look at the total.

Islands are a real benefit with a real cost, and the boundary is the decision

Making a component interactive means shipping its framework, its dependencies and the hydration work to the browser, whether the reader touches it or not. Astro lets you choose when it loads — immediately, when the browser is idle, when it scrolls into view — which softens the cost without removing it. What matters is that the boundary is chosen against what the component actually needs rather than against what made the page work during development.

A content collection is a schema, and a loose schema is not one

Typed collections are the part of Astro that most improves a site's reliability, because an entry that does not match the expected shape fails the build rather than rendering an empty field. That only holds while the schema is strict. A collection with everything optional accepts everything, which restores exactly the problem the types were there to prevent.

The rendering mode decides what a crawler gets, and then the cache decides how often

Static, server and hybrid rendering produce the same HTML and quite different operational behaviour: what is rebuilt when content changes, what the origin is asked for, and how quickly a correction reaches a reader. Astro's default is the strongest starting position there is for a crawler, and it is a starting position rather than a guarantee about a specific site.

What we do about it

Capabilities

Each of these is work we carry out, not an area we advise on.

Island boundary review

For each interactive element: what it does, whether it needs to be interactive at all, and if it does, what it needs from the browser. This is the single decision that determines the weight of the site, and it is made component by component with the whole surface visible rather than one component at a time.

Hydration directives chosen against behaviour

Which of `client:load`, `client:idle`, `client:visible` and `client:media` each island gets, decided by when the reader actually needs it rather than by which one first produced a working page. A directive chosen for convenience is a cost paid on every page load by every reader who never touches that component.

Content collections and schema design

Collections defined, schemas written strictly enough to reject a malformed entry, and relationships between collections expressed so that a reference which no longer resolves fails the build. The schema is the contract between the content and the templates, and being specific about it is the cheapest reliability available.

Rendering mode per route class

Which routes are generated at build time, which are rendered on request, and which need a hybrid arrangement because most of the estate is static and one part of it is not. Decided against how often the content changes and how quickly a change has to appear, rather than inherited as a global setting.

Data fetching and build behaviour

Where each route's data comes from, at which point in its life it is fetched, and what causes a rebuild. An Astro build can regenerate the entire estate for a change to a single page, and on a large collection that is the difference between a deploy measured in seconds and one measured in hours.

Search-engine delivery

What each route serves before any JavaScript runs, metadata and structured data per route, canonical URLs, sitemaps and redirects — verified on the built output rather than assumed from the framework's reputation, because a change of rendering mode alters all of them at once.

Third-party script accounting

Analytics, chat, consent and embedded media counted against the weight of the site, with each one's cost known and its loading deferred where it can be. These are the largest single contributors to a site that is otherwise light, and they are rarely reviewed because they arrive through a request rather than a code change.

Build and deployment

How the site is built, what is generated ahead of a deploy, how a deploy behaves for a reader mid-session, and how a content change reaches a running site. A static site's operational model differs from an application's, and it is worth understanding before it is depended on.

How we work

Engineering methodology

The sequence is deliberate. The order is usually what determines whether the work holds or has to be repeated.

  1. Establish what the site is before deciding what it should be

    Which parts are documents and which are genuinely applications. That classification is the design, and it is a separate question from which framework is used — a document served from a single-page application is an application paying for something it does not use.

  2. Make the interactive surface countable

    Every island listed, with what it does and why it needs the browser. For most sites the list is short, and its length is the finding: it turns a general sense that the site has grown heavy into a specific account of where the weight is.

  3. Prefer static, and require a reason to leave it

    Static rendering is the strongest default for the reader and the crawler alike. Where a route has to be rendered on request, the reason is recorded, so that a route which is dynamic by inheritance can be told apart from one that is dynamic because its content genuinely changes.

  4. Type the content strictly enough to fail

    Schemas written so that a missing or malformed field breaks the build rather than the page. A validation nothing can fail is not a validation, and a build that stops is a considerably cheaper discovery than a page that renders an empty field to a customer.

  5. Verify the delivered response, not the framework's promise

    What the HTML contains before hydration, whether the structured data matches the visible page, and whether the canonical and the redirect are right. Astro's defaults make these more likely to be correct; they do not make them checked.

  6. Keep the total visible as the site grows

    What is shipped, measured at intervals rather than once, because the failure mode here is gradual and every individual addition looks reasonable at the time. The number that matters is the total, and it is nobody's job unless it is made somebody's.

Deliverables

What an engagement produces

Documentation is a deliverable, not an afterthought. On most of these engagements a large part of the value is a defect report precise enough for another team to act on.

Design

  • Every interactive element listed, with what it does and whether it needs the browser
  • Hydration directive per island, chosen against when the reader needs it
  • Collection schemas, written to reject a malformed entry
  • Rendering mode per route class, with the reason recorded
  • What is rebuilt when content changes, and how long that takes

Build

  • Islands implemented with the boundaries as designed
  • Content collections defined, with content validated against them
  • Rendering modes configured per route class
  • Third-party scripts deferred where they can be, with the remaining cost stated
  • Metadata, structured data, canonicals, sitemaps and redirects generated from the content

Verification and handover

  • What each route delivers before JavaScript runs
  • Client JavaScript shipped per route, with the largest contributors named
  • Structured data checked against the visible page rather than in isolation
  • Redirects and canonicals verified on the response
  • Which decisions to revisit as the site grows, and what would prompt it
Under the hood

Architecture and technology

What decides whether a component becomes an island

  • Whether it needs to respond to the reader at all — a great many do not
  • Whether its state can be settled at build time and sent as HTML
  • How soon after load the reader needs it, which selects the directive
  • How much it brings with it: its framework, its dependencies and theirs
  • Whether it is above the fold, and therefore whether deferring it is visible
  • Whether the same interaction could be a link, a form post or a CSS state

Where Astro sites go wrong

  • Every component an island, reproducing the framework the project moved away from
  • `client:load` used throughout because it is the directive that always works
  • A collection schema with every field optional, so nothing is ever rejected
  • A build that regenerates the entire estate for a change to one entry
  • Fetching in the browser for content that was available at build time
  • Structured data emitted by a component that hydrates to different text
  • Third-party scripts on every page whose cost was never counted
  • Metadata, canonicals and redirects never verified on the response
Adjacent problems

If this is not quite your problem

These overlap at the edges. Sending you to the right page is more useful than having you work it out.

The architecture is the open question

Choosing and designing a decoupled stack, which is a different engagement.

Headless website development

The site is currently on Next.js

What moving it to Astro involves, and which parts are a port rather than a rebuild.

Next.js to Astro migration

The CMS side needs integrating

Connecting a chosen CMS to an existing frontend.

Headless CMS integration

The pages need to be found

What a crawler receives, what is indexable, and what is being wasted.

Technical SEO audit
Questions

Frequently asked

How is this different from your headless development page?

One is the architecture and this is the site. Headless website development covers whether to decouple at all, what the stack should be, and how content reaches the frontend — the decisions made before any code exists. This page is about building the Astro site once that is settled: which components are interactive, what each one needs from the browser, how the content is typed, and what a crawler receives. If you are still deciding whether a decoupled architecture is right, that is the other page. If you have one and the site needs engineering, it is this one.

Do you have an Astro site we can look at?

Yes — this one. We migrated our own site from a Next.js static export to Astro, and the result is what you are reading, which means the evidence is inspectable rather than described: the routes, the sitemap, the redirects and the structured data are all publicly served. It is also our own site rather than a client engagement, migrated for our own reasons on our own timeline, so it demonstrates that we have done the work and says nothing about working inside somebody else's constraints — which is what a client migration actually tests. That is why this page names no client work for it and describes the migration as work performed rather than as a result.

Should we move off Next.js to Astro?

That depends on how much of your site is an application. A content site built on Next.js is paying for a client runtime and framework machinery it mostly does not use, and Astro fits it well. A site with shared client state, client-side routing and genuinely interactive views is an application, and moving it to a document framework means rebuilding that behaviour rather than porting it — which may still be the right answer, and is a different project with a different cost. The classification comes before the recommendation, and it is done route by route.

How much JavaScript does an Astro site ship?

As much as its islands require and no more, which for a page with no interactive components is none. The framework adds no runtime of its own to a static page. The honest qualification is that this is a property of how the site is built rather than a property of choosing Astro: an island is a decision made by whoever writes that component, the decisions are not centrally visible, and a site can accumulate an application's worth of JavaScript one reasonable addition at a time. Keeping the total visible is part of the work rather than an outcome of the framework.

Which parts of our site actually have to be interactive?

Fewer than most sites assume. A good deal of what is built as an interactive component is a link, a form, a details element or a CSS state, and each of those costs the reader nothing. The ones that genuinely need the browser are usually a search field, a filter, something with optimistic state, or an embedded third-party widget. The useful exercise is listing every interactive element with what it does and why it needs to be interactive, because the list is what makes the total visible — and it is common for two or three of the entries to become static markup once the question is asked directly.

Bring us the problem you have not been able to fix

Describe what is happening rather than what you think the cause is. If we are not the right people for it, we will say so.