Build content sites
Prerender Astro pages, hydrate Ilha islands, and add small server routes.
Choose this template for content sites: landing pages, portfolios, blogs, event pages. Choose the app template for accounts, live state, or dashboards.
Route with files
- Add
src/pages/index.astrofor/andsrc/pages/blog/[slug].astrofor each post. - Add API routes as files:
src/pages/api/newsletter.tsserves/api/newsletter. - Keep frontmatter (
title,description,date) on every post;draft: truestays out of the build.
Hydrate islands where the page says
- Write interactive parts as plain Ilha components in
src/islands/. - Use
client:loadabove the fold andclient:idleorclient:visiblebelow it. - Keep island props JSON only.
- Test islands with
renderHtml(Component)fromilha/test.
Opt server routes out of prerendering
Pages prerender by default and no server runs for them. A route that needs bindings opts out with one switch:
// src/pages/api/newsletter.ts
import type { APIRoute } from "astro";
import { env } from "cloudflare:workers";
export const prerender = false;
export const POST: APIRoute = async () => {
const { DB } = env;
const { results } = await DB.prepare("SELECT id FROM newsletter").all();
return Response.json({ count: results.length });
};
- Set
export const prerender = falseon every route that reads bindings. - Read bindings only through
import { env } from "cloudflare:workers". - Never use
Astro.locals.runtime.env; Astro 6 removed it and it throws at runtime. - Declare new bindings on
Cloudflare.Envinsrc/env.d.ts. - Keep server routes few and small: at most one or two against D1.
Ship config and schema the celld way
- Edit
astro.config.mjs(celld-configintegration), never hand-writedist/wrangler.json. - Read derived keys from
dist/wrangler.jsonafterbun run build. - Commit migration files under
db/migrations/; never edit an applied migration. - Keep the route’s
CREATE TABLE IF NOT EXISTSas a local-dev backstop matching the migration.
For the person-side walkthrough, see Launch a content site.