On-Demand and Mixed Rendering
By Syncfusion®, Inc. · Last updated
How on-demand rendering works, server adapters, Astro.request, Astro.url, Astro.response, Astro.redirect(), returning a Response, dynamic routes without getStaticPaths(), the prerender export, mixing static and on-demand pages, a decision guide, request security, the 500 page and streaming.
On-Demand Rendering
- On-demand rendering = a server makes a page's HTML when a visitor asks for it.
- Each request can give a different page: a search result, a cart, a "Hello, Asha".
- Astro does this with an adapter and one line per page.
- In this lesson, Asha adds pages that react to each visitor, while the rest of her site stays static.
Simple lesson: on-demand pages are made per request, so they can use the request.
A Real-World Example
- Asha adds a search box to her blog.
- The results depend on what the reader types, so they cannot be built ahead of time.
- She adds the Node adapter and marks only the search page as on-demand.
- Her posts stay static and fast. Only search runs on the server.
Press ▶ Run. Type a word in the box and press Search.
What happened:
export const prerender = falsemade the page on-demand.- The form sent
?q=...in the address. The page read it withAstro.url.searchParams. - Every search is a new request. The Terminal shows each one.
- Lines tagged
[server]come from the server, while it handles a request. Aconsole.login an on-demand page prints there too, not in the browser.
What You Will Learn
In this lesson, you will learn:
- How a request becomes a response.
- What adapters do, and how to add one.
- How to read the request and change the response.
- How to redirect, or return a
Responseyourself. - How dynamic routes work without
getStaticPaths(). - How to mix static and on-demand pages with
prerender. - How to choose a mode for each page.
- How Astro protects forms, and how to show a 500 page.
How On-Demand Rendering Works
Request, Render and Response
- A visitor's browser sends a request: the address, the method (GET or POST), headers and cookies.
- The server runs the page's component script with that request.
- The server sends back a response: a status code, headers and the HTML.
Animation: a request travels from the browser to the server, the server runs the page, and a response with HTML travels back.
Browser /search?q=astro
Server runs search.astro
1. Request: GET, address, cookies 3. Response: 200, HTML
2. Render
Each visit is one request and one response.
Static Files vs a Running Server
- A static page is a file. The host sends it without running anything.
- An on-demand page needs a running server that executes your code.
- That server costs more and may be a little slower. Use it only where a page needs it.
Simple lesson: the server runs your page once per request, and sends back a response.
Server Adapters
What Adapters Do
- Adapter = a package that lets Astro's server code run on a specific host.
- Each host runs servers differently. The adapter does the translation.
Official Adapters
@astrojs/node= your own server, or any host that runs Node.js.@astrojs/vercel,@astrojs/netlify,@astrojs/cloudflare= those hosts' serverless platforms.
Choosing an Adapter
- Pick the adapter of the host you will deploy to.
- Not sure yet? Start with
@astrojs/node. You can switch later with one line.
Your pages do not change when you switch adapters. Only the adapter line in the config changes, so the choice is easy to undo.
Adding an Adapter with astro add
npx astro add node
- It installs the package and sets
adapterin the config:
import node from "@astrojs/node";
export default defineConfig({
adapter: node({ mode: "standalone" }),
});
mode: "standalone"builds a server you start withnode ./dist/server/entry.mjs.
Press ▶ Run. This page asks to be on demand, but there is no adapter. Read the Terminal, then add the adapter lines from above.
Simple lesson: one adapter per host. npx astro add <host> sets it up.
Working with Requests and Responses
Astro.request
Astro.requestis a standard web Request object.Astro.request.method="GET","POST"and so on.Astro.request.headers.get("user-agent")= one request header.await Astro.request.formData()= the fields of a submitted form.
Astro.url
Astro.urlis a URL object for the current address.Astro.url.pathname= the path, such as/search.Astro.url.searchParams.get("q")= one value from the query string.
Astro.clientAddress
- The visitor's IP address, as the server sees it. Some adapters do not provide it.
Astro.response
Change the response before it is sent:
Astro.response.status = 404;Astro.response.headers.set("Cache-Control", "max-age=60");
Astro.redirect()
return Astro.redirect("/login");sends the visitor somewhere else.- The default status is
302(temporary). Pass301for a permanent move. - Use
return, so the rest of the page does not run.
Press ▶ Run. Fill the form and send it. Then click the "old" link.
- Send the form empty: the status becomes
400. The Terminal shows it. - The old link answers with a
301redirect to/contact.
Returning a Response from a Page
- A page can
return new Response(...)instead of HTML. - Use it for a plain-text or JSON answer, or a custom status.
- Example:
return new Response("Not found", { status: 404 });
Fetching Fresh Data on Every Request
- In an on-demand page,
fetch()runs on each visit. - Readers always see the latest data. Each visit also waits for the API.
HTML Streaming
- Streaming = the server sends the HTML in pieces, as each piece is ready.
- Astro streams on-demand pages by default. The top of the page can show while a slow part below is still loading.
- Put slow
fetch()calls in components lower on the page, not all at the top.
Simple lesson: read Astro.request and Astro.url, change Astro.response, and return a redirect or a Response when needed.
On-Demand Dynamic Routes
Dynamic Routes Without getStaticPaths()
- In a static page,
getStaticPaths()must list every page ahead of time. - In an on-demand page, there is no list. Any value in the address is accepted.
- The page reads
Astro.paramsand looks up the data on each request. - If nothing is found, return a 404.
On-demand dynamic routes have no getStaticPaths(). Instead of a list, you must handle "not found" yourself.
Simple lesson: on-demand [slug] pages accept any value. Look it up, and return a 404 when it is missing.
Mixed Rendering
Why Mix
- Most pages of a site are the same for everyone: static is fastest.
- A few pages need the request: on-demand is needed.
- Mixed rendering = both in one project. Static speed where possible, on-demand where needed.
The prerender Export
export const prerender = false;= render this page on demand.export const prerender = true;= build this page ahead of time.- The
outputsetting decides the default.prerenderchanges it for one page.
Two Ways to Mix
- A static project with some on-demand pages = the default
output: "static", plusprerender = falseon those pages. Most sites look like this. - An on-demand project with some static pages =
output: "server", plusprerender = trueon pages that never change.
Press ▶ Run. Read the Terminal: ▶ pages were built now, λ pages run on each request.
Choosing the Right Mode
A Decision Guide
Ask one question for each page: when does its content change?
- Per visitor (their name, their cart, their settings) → on-demand.
- Per request (search results, query strings, form answers) → on-demand.
- Every few minutes (live scores, stock levels) → on-demand, perhaps with caching.
- Rarely (posts, docs, about pages) → static.
A Comparison Table
| Point | Static | On-demand | Mixed |
|---|---|---|---|
| Hosting | Any static host | A server or serverless host with an adapter | A host with an adapter |
| Speed | Fastest | Depends on the server and APIs | Fast where static, fresh where needed |
| Data freshness | As of the last build | Every request | Each page chooses |
| Cost | Lowest | Higher: code runs per visit | Low: only some pages run code |
| Request data | No | Yes | Only on on-demand pages |
Simple lesson: start static. Make a page on-demand only when its content depends on the visitor or the request.
Request Security and Error Pages
Cross-Site Request Forgery Protection
- CSRF (cross-site request forgery) = a bad website tricks a visitor's browser into sending a form to your site.
- Astro checks this for on-demand pages. It is on by default:
security.checkOrigin. - A form
POSTthat comes from a different website is refused.
What the 403 Response Means
- 403 Forbidden = "I understood the request, but I refuse it".
- If a real form gets a 403, check that it is sent from your own site's address.
The 500 Page
- 500 = "the server had an error".
- Make
src/pages/500.astro. Astro shows it when an on-demand page throws an error. - It gets an
errorprop. Do not show error details to visitors on a live site. Log them instead.
Press ▶ Run. The broken page throws an error, so the 500 page is shown.
On a live site, never print error.message on the 500 page. It can reveal details about your server. Show a friendly message and log the error for yourself.
Simple lesson: Astro blocks cross-site form posts with a 403. A 500.astro page catches server errors kindly.
TL;DR
- On-demand rendering = HTML made per request on a server.
- Adapter = runs Astro on your host.
npx astro add node(or vercel, netlify, cloudflare). - Astro.request = method, headers,
formData(). Astro.url = pathname,searchParams. - Astro.response = set
statusandheaders. - Astro.redirect(path, status) =
returnit. 302 by default. - return new Response(...) = a custom answer, such as a 404.
- On-demand [slug] = no
getStaticPaths(). Handle "not found" yourself. - prerender =
falsefor on-demand,truefor static, per page. - Choose = static unless the content depends on the visitor or the request.
- Security =
checkOriginrefuses cross-site form posts with a 403. 500.astro = the error page.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Astro.redirect() without return | Calling it stops the page | Write return Astro.redirect(...) | The page keeps running and may fail |
| No adapter | Astro has a built-in server | Add one with astro add | The build stops |
getStaticPaths() in an on-demand page | Every dynamic page needs it | On-demand pages read Astro.params | It is ignored, with a warning |
| Not handling a missing item | Every address has data | Return a 404 when nothing is found | The page crashes with a 500 |
| Making the whole site on demand | It is more modern | Mix: on-demand only where needed | Slower pages, higher bills |
Showing error.message to visitors | It helps users | Log it, show a friendly message | Server details leak |
Check Yourself
Exercises
What to Remember
- On-demand pages run on a server, once per request. They need an adapter.
- Read the request with
Astro.requestandAstro.url. Change the answer withAstro.response. return Astro.redirect()orreturn new Response()to answer differently.- Mix modes with
prerender. Start static, opt in where needed. - Astro blocks cross-site form posts. A
500.astropage handles server errors.
Next: Astro Islands, where Asha adds interactive components from Preact, with JavaScript only where they live.