# How Astro Renders Pages

Course: [Astro](https://www.devtutorials.ai/learn/astro) · Section: Rendering
Source: https://www.devtutorials.ai/learn/astro/rendering/how-astro-renders

> What rendering means, build time vs request time vs the browser, SSG, SSR, CSR and hydration, Astro's static and on-demand modes, client-side islands, static rendering in depth, keeping static sites fresh, the output setting and why hybrid was removed.

## What Rendering Means

- **Rendering** = turning your components and data into HTML that a browser can show.
- Every page must be rendered at some point. The big question is **when** and **where**.
- The answer decides how fast the page is, how fresh its data is, and what it costs to host.
- In this lesson, you will learn the three places rendering can happen, and the words people use for them.

**Simple lesson:** rendering makes the HTML. Where and when it happens shapes the whole site.

### A Real-World Example

- Asha's blog posts change maybe once a week. They can be made into HTML once, ahead of time.
- Next month she adds a page that greets the logged-in reader by name. That page is different for every visitor.
- The same site now needs two ways of rendering.
- Astro lets each page choose.

Press **▶ Run**. Then switch between the two pages with the links. Watch the times.

<p>This HTML was made at <strong>{builtAt}</strong>.</p>
<a href="/about">About</a>
`,
    "src/pages/about.astro": `---
const builtAt = new Date().toLocaleTimeString("en-IN");
---

<h1>About</h1>
<p>This HTML was made at <strong>{builtAt}</strong>.</p>
<a href="/">Home</a>
`,
  }}
/>

What happened:

- Both pages were rendered once, during the build. The **Terminal** shows it.
- Clicking the links does not change the times. The browser only shows the ready HTML.
- Press **▶ Run** again: that is a new build, so the times change.

### What You Will Learn

In this lesson, you will learn:

1. Where and when rendering can happen.
2. What SSG, SSR, CSR and hydration mean.
3. Astro's two render modes, and where client-side rendering fits.
4. What static rendering can and cannot do.
5. How static sites stay up to date.
6. What the `output` setting does.
7. How to see the difference yourself.

## Where and When Rendering Happens

There are three moments:

- **Build time** = on your computer (or a build server), when you run `astro build`. Before anyone visits.
- **Request time** = on a server, when a visitor asks for the page.
- **In the browser** = on the visitor's device, after the page has loaded, by running JavaScript.

<figure className="ast-fig ngc-fig">
<svg viewBox="0 0 660 230" role="img" aria-labelledby="fig-when" style={{ width: "100%", maxWidth: 660, height: "auto" }}>
  <title id="fig-when">Animation: a timeline with three stops. Build time happens once before visitors. Request time happens on the server for each visit. The browser runs JavaScript after the page arrives.</title>
  <line x1="40" y1="110" x2="620" y2="110" style={{ stroke: "var(--ngc-slate)", strokeWidth: 2 }} />
  <g style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 14 }}>
    <circle cx="120" cy="110" r="14" style={{ fill: "var(--ngc-purple)" }} />
    <text x="120" y="80" textAnchor="middle" style={{ fill: "var(--ngc-purple)" }}>Build time</text>
    <circle cx="330" cy="110" r="14" style={{ fill: "var(--ngc-orange)" }} />
    <text x="330" y="80" textAnchor="middle" style={{ fill: "var(--ngc-orange)" }}>Request time</text>
    <circle cx="540" cy="110" r="14" style={{ fill: "var(--ngc-blue)" }} />
    <text x="540" y="80" textAnchor="middle" style={{ fill: "var(--ngc-blue)" }}>In the browser</text>
  </g>
  <g style={{ fontFamily: "var(--sans)", fontSize: 12, fill: "var(--ink2)" }}>
    <text x="120" y="148" textAnchor="middle">Your computer</text>
    <text x="120" y="166" textAnchor="middle">Once, before visitors</text>
    <text x="330" y="148" textAnchor="middle">A server</text>
    <text x="330" y="166" textAnchor="middle">On every visit</text>
    <text x="540" y="148" textAnchor="middle">The visitor's device</text>
    <text x="540" y="166" textAnchor="middle">After the page loads</text>
  </g>
  <circle r="8" cy="110" style={{ fill: "var(--ngc-green)" }}><animate attributeName="cx" values="40;120;120;330;330;540;540;620" keyTimes="0;0.12;0.3;0.42;0.6;0.72;0.9;1" dur="7s" repeatCount="indefinite" /></circle>
  <text x="330" y="210" textAnchor="middle" style={{ fill: "var(--ngc-slate)", fontFamily: "var(--sans)", fontSize: 12 }}>The earlier the HTML is made, the less work is left for each visit.</text>
</svg>
<figcaption>Three moments when HTML can be made.</figcaption>
</figure>

**Simple lesson:** HTML can be made at build time, at request time, or in the browser.

## Rendering Terms Explained

You will see these four terms in every framework's docs. Here they are in plain words.

### Static Site Generation (SSG)

- **SSG** = make every page's HTML at **build time**.
- The result is a folder of plain files. Any web host can serve them.
- Very fast, very cheap, very safe. But the data is only as fresh as the last build.

### Server-Side Rendering (SSR)

- **SSR** = make a page's HTML on a **server**, at **request time**.
- Each visitor can get a different page: their name, their cart, today's prices.
- Needs a running server, which costs more and is a little slower.

### Client-Side Rendering (CSR)

- **CSR** = send almost empty HTML plus JavaScript. The **browser** builds the page.
- Good for very interactive apps. But the first view waits for the JavaScript, and search engines see less.

### Hydration

- **Hydration** = the browser runs JavaScript on HTML that already exists, to make it interactive.
- The HTML came from SSG or SSR. Hydration "adds water": it attaches click handlers and state.
- In Astro, only small **islands** are hydrated, not the whole page. You meet them in **Astro Islands**.

SSR and hydration are often confused. SSR makes HTML on a server. Hydration runs JavaScript in the browser on HTML that already arrived. A page can use one, both, or neither.

<div className="tscroll">

| Term | Where the HTML is made | When | Main strength | Main cost |
|---|---|---|---|---|
| SSG | Your computer | Build time | Fastest and cheapest | Data waits for the next build |
| SSR | A server | Each request | Fresh, personal pages | Needs a server |
| CSR | The visitor's browser | After load | Rich interactivity | Slow first view, more JavaScript |

</div>

**Simple lesson:** SSG builds ahead, SSR builds per request, CSR builds in the browser. Hydration wakes up HTML that already exists.

## Astro's Render Modes

### Static Rendering

- Astro's name for SSG.
- It is the **default**. Every page so far in this course was static.

### On-Demand Rendering

- Astro's name for SSR.
- A page is rendered on demand when you turn it on for that page, or for the whole site.
- It needs an **adapter**: a small package that runs Astro on your host's server. You add one in **On-Demand and Mixed Rendering**.

### Client-Side Rendering in Astro

- Astro does not render whole pages in the browser.
- But an island can render only in the browser with `client:only`. Use it for parts that cannot run on a server, such as a map that needs `window`.

The lab below has one page of each kind. Press **▶ Run**, open **HTML sent to browser**, then visit `/live` twice.

<p>Made at build time: {at}</p>
<p><a href="/live">On-demand page</a> · <a href="/browser">Browser page</a></p>
`,
    "src/pages/live.astro": `---
const at = new Date().toLocaleTimeString("en-IN");
---

<h1>On-demand page</h1>
<p>Made on request: {at}</p>
<p><a href="/live">Visit again</a> · <a href="/">Home</a></p>
`,
    "src/pages/browser.astro": `<h1>Browser page</h1>
<p id="out">Loading…</p>
<a href="/">Home</a>

<script>
    document.getElementById("out")!.textContent =
      "Made in the browser: " + new Date().toLocaleTimeString("en-IN");
</script>
`,
  }}
/>

What to compare:

- **Static page:** the time never changes until the next build.
- **On-demand page:** each visit makes new HTML. The **Terminal** counts the requests.
- **Browser page:** the **HTML sent to browser** only says "Loading…". The text is made later by the script.

In this lab, the "server" runs inside the preview. On a real host, on-demand pages run on the host's server, and the adapter connects Astro to it.

**Simple lesson:** static by default, on-demand when a page needs request data, browser-only for small islands.

## Static Rendering in Depth

### The Build Step Renders Every Page Once

- `astro build` runs every component script one time per page.
- For dynamic routes, `getStaticPaths()` decides how many pages there are.

### The Output Is Plain Files

- The `dist/` folder holds `.html`, `.css`, `.js` and image files.
- **Static host** = a service that only serves files: GitHub Pages, Netlify, Cloudflare Pages, Vercel and many more.

### No Server Runs When a Visitor Opens the Page

- The host just sends the file. Nothing of yours runs.
- That makes static sites fast, cheap and hard to attack.

### Request Data Needs On-Demand Rendering

- A static page is made before any visitor exists.
- So it cannot read the visitor's cookies, headers, session or query string.
- Example: `?name=Asha` in the address does nothing on a static page. It was already built.

Press **▶ Run**. Both links add `?name=Asha`. Only the on-demand page can read it.

<ul>
    <li><a href="/static-hello?name=Asha">Static page with ?name=Asha</a></li>
    <li><a href="/live-hello?name=Asha">On-demand page with ?name=Asha</a></li>
</ul>
`,
    "src/pages/static-hello.astro": `---
const name = Astro.url.searchParams.get("name") ?? "friend";
---

<h1>Hello, {name}!</h1>
<p>Built once. The query string did not exist yet.</p>
<a href="/">Back</a>
`,
    "src/pages/live-hello.astro": `---
const name = Astro.url.searchParams.get("name") ?? "friend";
---

<h1>Hello, {name}!</h1>
<p>Built for this request, so it can read the query string.</p>
<a href="/">Back</a>
`,
  }}
/>

**Simple lesson:** a static page knows nothing about the visitor. Anything per-request needs on-demand rendering.

## Data at Build Time

### Data Does Not Change Until the Next Build

- A static page shows the data from the moment it was built.
- New posts, new prices, new comments: none appear until you build again.

<ul>
    {posts.map((p) => <li>{p.title}: ♥ {p.likes}</li>)}
</ul>
<p><small>New likes appear after the next build.</small></p>
`,
  }}
/>

- The **Terminal** shows one API call, during the build.
- However many readers visit, the API is not called again.

### Keeping Static Sites Fresh

- **Rebuild on push** = most hosts rebuild when you push to Git.
- **Webhook** = a web address that starts a build. A CMS can call it when someone presses "Publish".
- **Scheduled build** = rebuild at set times, such as every night, with your host or GitHub Actions.
- For data that changes every minute, use on-demand rendering instead.

A static site that rebuilds every time content changes is usually fast enough. Reach for on-demand rendering only when the data is personal or changes faster than you can rebuild.

### Static Rendering Use Cases

- Blogs and documentation sites.
- Marketing and landing pages.
- Portfolios and personal sites.

**Simple lesson:** static data is fresh as of the last build. Webhooks and schedules keep it up to date.

## The output Setting

### output: "static" (Default)

- Every page is static, unless the page says `export const prerender = false`.
- You do not need to write it. It is the default.

### output: "server"

- Every page is on demand, unless the page says `export const prerender = true`.
- Use it when most of your site needs request data, such as a dashboard app.
- It needs an adapter.

Press **▶ Run**. The config says `output: "server"`. Look at the **Terminal**: `λ` marks on-demand pages, `▶` marks static ones.

<p>Rendered on every request.</p>
<a href="/about">About</a>
`,
    "src/pages/about.astro": `---
---

<h1>About</h1>
<p>This page never changes, so it is built ahead of time.</p>
<a href="/">Dashboard</a>
`,
  }}
/>

### Why hybrid Is No Longer Needed

- Astro 4 had a third value, `output: "hybrid"`, for "mostly static, some on-demand".
- Since Astro 5, `"static"` already allows on-demand pages with `prerender = false`. So `hybrid` was removed.
- If an old guide says `hybrid`, use `"static"` (the default) instead.

**Simple lesson:** `output` picks the default for every page. `prerender` changes it for one page.

## TL;DR

1. **Rendering** = turning components and data into HTML.
2. **Three moments** = build time, request time, in the browser.
3. **SSG** = HTML at build time. **SSR** = HTML per request on a server. **CSR** = HTML built in the browser.
4. **Hydration** = JavaScript waking up existing HTML. Astro hydrates only islands.
5. **Astro's modes:**
   - static rendering = SSG, the default,
   - on-demand rendering = SSR, needs an adapter,
   - `client:only` islands = browser-only parts.
6. **Static limits** = no cookies, headers, sessions or query strings.
7. **Fresh static sites** = rebuild on push, webhooks, scheduled builds.
8. **output** = `"static"` (default) or `"server"`. `hybrid` was removed.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Reading `?name=` on a static page | Every page sees the request | Use on-demand rendering for request data | The value is always empty |
| Expecting new data on a static page | Pages update by themselves | Rebuild, or render on demand | Readers see old data |
| Making every page on demand | SSR is always better | Default to static, opt in per page | Slower pages and higher hosting costs |
| `output: "hybrid"` | Old guides are current | Use `"static"` with `prerender = false` | Config error in Astro 5 and later |
| On-demand pages with no adapter | Astro runs its own server | Add an adapter | The build stops |
| Calling static pages "slow to update" a bug | Static means broken | It is a trade-off you choose | You add servers you do not need |

</div>

## Check Yourself

## Exercises

## What to Remember

- Rendering makes HTML at build time, at request time, or in the browser.
- SSG is fast and cheap. SSR is fresh and personal. CSR is for rich interactivity.
- Astro is static by default. On-demand pages need an adapter.
- Static pages cannot see cookies, headers or query strings.
- `output` sets the default. `prerender` changes it per page.

Next: **On-Demand and Mixed Rendering**, where Asha adds an adapter and pages that react to each visitor.
