Skip to content
Astro interview 4 min read

Rendering & SSR Questions

Rendering is where Astro’s “zero JavaScript by default” philosophy meets real deployment constraints. Interviewers use this area to check whether you understand when HTML is produced — at build time, on each request, or somewhere in between — and how adapters wire Astro into a server runtime. The questions below cover the rendering modes, the SSG-versus-SSR trade-off, and per-route control.

What are Astro’s rendering modes?

Astro 5 has three output behaviors, controlled per route rather than globally. By default every page is prerendered (static HTML generated at build time). When you add a server adapter, individual routes can opt into on-demand rendering (SSR), where the HTML is built per request on a server. The third mode is hybrid: most of the site is static, but a few routes render on demand — this is the common case for content sites with a small dynamic surface.

A typical interview answer should stress that there is no single global “SSR mode” anymore. You install an adapter, then mark only the routes that need request-time data as server-rendered.

// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";

export default defineConfig({
  adapter: node({ mode: "standalone" }),
});
---
// src/pages/dashboard.astro — opt this route INTO on-demand rendering
export const prerender = false;

const res = await fetch("https://api.example.com/me", {
  headers: { cookie: Astro.request.headers.get("cookie") ?? "" },
});
const user = await res.json();
---
<h1>Welcome, {user.name}</h1>

Reverse the default with output: "server" in the config, then mark static pages with export const prerender = true. Pick whichever default minimizes per-route overrides.

What is the difference between SSG and SSR?

This is the most common question in the section. The cleanest framing: SSG decides when the work happens, SSR decides where.

AspectSSG (prerender)SSR (on-demand)
When HTML is builtOnce, at astro buildOn every request
Where it runsCI / build machineA server or edge runtime
Adapter requiredNoYes
Access to Astro.requestNo (no real request)Yes (headers, cookies, URL)
PersonalizationNone — same HTML for allPer-user, per-request
LatencyCDN-fast, cacheableDepends on server + data
Best forBlogs, docs, marketingAuth dashboards, carts, APIs

In both modes Astro ships zero JavaScript by default — the rendering mode only affects how the HTML is produced, not how much JS the browser downloads. Interactivity still comes from islands hydrated with client:* directives, independent of SSG vs SSR.

What is an adapter and why is it required for SSR?

An adapter is an integration that teaches Astro how to produce a server entry point for a specific runtime — Node, Cloudflare, Netlify, Vercel, Deno, and so on. Without an adapter Astro has no Request → Response handler to deploy, so prerender = false routes throw a build error. The adapter converts Astro’s internal rendering into the host’s expected signature (a Node HTTP server, a Cloudflare fetch handler, a Vercel function, etc.).

npx astro add node
# or: cloudflare, netlify, vercel, deno
// astro.config.mjs — Cloudflare example
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";

export default defineConfig({
  adapter: cloudflare(),
});

A good follow-up answer notes that the adapter also gives you access to platform primitives — Astro.locals.runtime on Cloudflare, for instance — and determines whether your dynamic routes run at the edge or in a regional function.

How do you return non-HTML responses or set status codes?

On-demand routes can return any Response, which is how you build API endpoints and handle redirects, errors, and content negotiation. This is a frequent practical question.

// src/pages/api/health.ts
import type { APIRoute } from "astro";

export const prerender = false;

export const GET: APIRoute = async () => {
  return new Response(JSON.stringify({ ok: true }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
};
---
// Redirect or 404 from inside a page
const post = await getPost(Astro.params.slug);
if (!post) return new Response(null, { status: 404 });
---
<article>{post.title}</article>

Output:

GET /api/health  →  200  {"ok":true}

When would you choose hybrid rendering?

Hybrid is the pragmatic default for most real sites. You prerender everything cacheable — landing pages, blog posts, docs — and flip only the handful of personalized or data-fresh routes to prerender = false. This keeps the CDN doing most of the work while still supporting logins, checkout, or live data. Interviewers like candidates who reach for hybrid instead of making the whole site SSR “just in case.”

Static-but-dynamic-looking pages can stay prerendered and fetch data client-side from an island, avoiding a server entirely. Reach for SSR only when you need request context (cookies, headers) at render time.

Best Practices

  • Default to prerendering and opt individual routes into SSR with export const prerender = false — don’t make the whole site server-rendered by reflex.
  • Choose the adapter that matches your deployment target before writing SSR code, since platform locals and runtime APIs differ.
  • Keep secrets and authenticated fetch calls in the server-rendered frontmatter, never in client islands.
  • Use API routes (APIRoute) for JSON and webhooks instead of bending .astro pages into endpoints.
  • Set explicit Cache-Control headers on SSR responses so the CDN can still cache safely cacheable dynamic pages.
  • Remember that rendering mode is orthogonal to client JS — islands and client:* directives behave the same under SSG and SSR.
Last updated June 14, 2026
Was this helpful?