Tutorial: Astro API and Islands
Purpose and Scope
This page continues the tutorial path from static Astro pages into interactive islands. In Astro terminology, an island is an enhanced UI component placed inside an otherwise mostly static page. The official tutorial introduces this by adding a UI framework such as Preact, building a small interactive greeting component, and deciding when client-side interactivity is worth the extra JavaScript. That same mental model also prepares you for server islands, where a component is deferred and fetched separately so the rest of the page can render and cache aggressively.
The important distinction is where the work happens. A client island uses a frontend framework component and a client directive to hydrate interactivity in the browser. A server island is a server-rendered Astro component marked for delayed rendering, commonly with fallback content while the dynamic fragment is fetched. The source files for this page show how Astro implements the server-island half of that story: discovered components are tracked during Vite processing, exposed through a virtual manifest, rendered as runtime instructions, and served through an internal endpoint. Sources: packages/astro/src/core/server-islands/vite-plugin-server-islands.ts, packages/astro/src/runtime/server/render/server-islands.ts, packages/astro/src/core/server-islands/endpoint.ts
Relevant Source Files
- packages/astro/src/core/server-islands/endpoint.ts — Defines the internal
/_server-islands/[name]route, request parsing for server-island render data, request size limits, and response status behavior. - packages/astro/src/core/server-islands/shared-state.ts — Stores discovered server islands, deduplicates them by resolved component path, assigns stable island names, and creates manifest source maps used at runtime.
- packages/astro/src/core/server-islands/vite-plugin-server-islands.ts — Implements the Vite plugin that discovers server components, requires an adapter for server islands, emits chunks for SSR builds, and serves the virtual server-island manifest.
- packages/astro/src/runtime/server/render/server-islands.ts — Renders deferred islands into the page by writing fallback content, runtime instructions, encrypted request data, and the client-side script that fetches the island.
- .changeset/sharp-bags-build.md — Records a deployment-facing fix for Cloudflare prerender failures so rendering errors in workerd are surfaced during
astro build. - configs/tsconfig.build.json — Shows the shared build TypeScript configuration that compiles each package from
srctodistand stores TypeScript build info underdist/._cache.
Core Primitives
The first primitive is the framework island: a component from Preact, React, Svelte, Vue, Solid, or another supported renderer that is embedded in an Astro page. The tutorial’s Preact greeting is the smallest useful example: the surrounding page can remain HTML-first while the greeting owns its interactive state. This keeps interactivity local. You should reach for a client island when the user needs browser behavior, such as clicks, local state, animations, forms, carousels, or embedded single-page application regions.
The second primitive is the server island. A server island is still an Astro component, but it is delayed rather than hydrated. The server-island renderer treats certain properties as internal markers, including server:component-path, server:component-export, server:component-directive, and server:defer. When those markers are present, Astro renders a placeholder path through ServerIslandComponent instead of immediately inlining the final component output. That class writes a render instruction, preserves fallback slot output, and injects a module script associated with a generated island host id. Sources: packages/astro/src/runtime/server/render/server-islands.ts
The third primitive is the server-island endpoint. Astro creates an internal route named /_server-islands/[name] backed by _server-islands.astro. That route is inserted at the front of the route manifest and marked as an internal, non-prerendered page. The endpoint accepts encrypted component export, props, and slots from either query parameters or a JSON request body. It rejects unsupported methods, malformed JSON, missing GET parameters, oversized bodies, and plaintext sensitive fields such as unencrypted slots or component exports. Sources: packages/astro/src/core/server-islands/endpoint.ts
Learning Flow
Start with a normal Astro page and ask which part genuinely needs interactivity. For a greeting widget, counter, menu, or theme switcher, the answer is usually a client island: keep the page static, import the framework component, and hydrate only that component. This is the tutorial lesson behind “living that island life.” You are not turning the entire site into an SPA; you are choosing a small region where browser JavaScript has a clear purpose.
Next, ask whether the dynamic behavior belongs on the client or the server. Personalized data such as a user avatar, account-specific navigation, or cookie-dependent content may need server access but should not delay the whole page. A server island fits that case. The page can stream or render fallback content first, then the island fetches its own rendered HTML. This complements the client-island model: client islands specialize in browser interactivity, while server islands specialize in delayed, personalized, server-rendered fragments.
When building a tutorial project, keep props simple and serializable. Server islands transfer render data over a network boundary, so functions and circular structures are not appropriate. The implementation reinforces this boundary by encrypting props, component export information, and slots before sending them to the endpoint. If the generated request can fit under the URL-length limit, the renderer can use search parameters; otherwise it can rely on request-body handling. The endpoint’s default body size limit is one megabyte unless the manifest supplies a different serverIslandBodySizeLimit. Sources: packages/astro/src/runtime/server/render/server-islands.ts, packages/astro/src/core/server-islands/endpoint.ts
System-to-Code Mapping
| Tutorial concept | Source-backed implementation |
|---|---|
| Island discovery | vitePluginServerIslands() scans Astro metadata for serverComponents and records each discovered component. |
| Stable island names | ServerIslandsState.discover() deduplicates by resolvedPath and preserves the first discovered record. |
| Runtime manifest | virtual:astro:server-island-manifest exports serverIslandMap and serverIslandNameMap placeholders that are replaced during transforms. |
| Deferred rendering | ServerIslandComponent.render() writes fallback slot content and a module script with a generated island id. |
| Internal fetch route | SERVER_ISLAND_ROUTE is /_server-islands/[name], injected into the route manifest before user routes. |
| Request validation | getRequestData() accepts GET or POST, rejects plaintext sensitive fields, returns 413 for body-size failures, and returns 405 for other methods. |
The Vite plugin is the bridge between authoring and runtime. During transforms, it reads Astro plugin metadata and discovers server components. If a component uses server-island behavior without an adapter, the plugin raises NoAdapterInstalledServerIslands, because delayed server rendering requires a runtime target that can answer the island request. During SSR builds, the plugin emits each island as a Rollup chunk and records the resulting reference id so the final manifest can map stable island names to emitted files. Sources: packages/astro/src/core/server-islands/vite-plugin-server-islands.ts, packages/astro/src/core/server-islands/shared-state.ts
ServerIslandsState keeps that mapping deterministic. Discovery is keyed by resolved component path, so importing the same island from multiple places does not create duplicate runtime names. If two different components have the same local name, the state appends an index to keep names unique. It can then generate import-map source from discovered paths for non-SSR and development paths, or from Rollup reference ids for SSR build output. This is why the author-facing API can stay simple while the build output remains chunk-aware. Sources: packages/astro/src/core/server-islands/shared-state.ts
Runtime Execution Flow
A deferred island begins as component metadata attached during Astro compilation. The Vite plugin notices that metadata, records the component, and ensures the server-island manifest can later resolve an island name to a module import. At render time, ServerIslandComponent reads the internal component path and export from props, looks up the generated name map through the SSR result, generates a unique host id, and prepares encrypted request values. It also adds content security policy digests when a CSP destination is present, including a digest for the runtime replacer script and the island content script. Sources: packages/astro/src/runtime/server/render/server-islands.ts
The page response then contains a placeholder protocol rather than the final island HTML. Astro writes a server-island render instruction, an Astro-only comment marker, any fallback slot content, and a module script tagged with data-astro-rerun and the island id. The endpoint later receives the encrypted component export, props, and slots, decrypts them, renders the requested component, and returns the island output. The result is a page that can show stable content quickly while postponing slower personalized content until its own request resolves. Sources: packages/astro/src/core/server-islands/endpoint.ts, packages/astro/src/runtime/server/render/server-islands.ts
Build and Deployment Signals
Server islands depend on the same package build conventions as the rest of the Astro monorepo. The shared TypeScript build configuration extends the base config, compiles package sources from src into dist, and stores incremental TypeScript build metadata under dist/._cache/ts_build/build.tsbuildinfo. That matters for contributors because the server-island files are authored as TypeScript source and published through package build output rather than treated as standalone scripts. Sources: configs/tsconfig.build.json
Deployment behavior also matters because server islands require adapters. The Cloudflare changeset in this source set is not a tutorial feature, but it is a useful signal for server-rendered islands: prerender and runtime failures must be surfaced reliably during astro build, especially in workerd-based environments. The fix described there buffers the response body before returning it to the build process so streaming render errors are caught and reported instead of silently producing truncated HTML. Sources: .changeset/sharp-bags-build.md
Next Steps
After building the tutorial greeting island, practice classifying components before adding directives. If the component only needs browser state, use a client island with the appropriate UI framework integration. If it needs server-only data and should not block the full page, use a server island with an adapter and fallback content. Then read the framework components, client-side scripts, server-side rendering, and on-demand rendering pages to connect the authoring model to deployment choices. The same island discipline scales from the tutorial blog to production sites: ship HTML by default, add JavaScript or delayed rendering only where the user experience requires it.