Fetching Data in Frontmatter
Calling fetch() in a component's frontmatter runs at build/request time, never in the visitor's browser.
2 min de lectura
A component's frontmatter is just JavaScript that runs before the template renders — which means anything you can do in a Node-like environment, including fetch(), await, and filesystem access, is available there. Understanding when and where that code runs is the key to using it well.
await right in the frontmatter
Astro components can be async implicitly — you can await directly in the frontmatter without wrapping it in an extra function:
---
const response = await fetch("https://api.example.com/posts");
const posts = await response.json();
---
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>That fetch call runs on the server (or at build time, in static mode) — never in the visitor's browser. The visitor's network tab shows no request to api.example.com at all; by the time the HTML reaches them, the data has already been fetched and rendered into markup. This is a meaningfully different model from client-side data fetching (a useEffect with fetch in React, say), where the browser makes the request after the page has already loaded, often showing a loading spinner in the meantime.
Why this matters
Fetching in frontmatter has a few concrete advantages over fetching in the browser:
- No loading state to design for. The HTML the browser receives already contains the data — there's no flash of "Loading..." because there was never a moment when the page existed without it.
- API keys stay secret. A
fetchcall using a private API key is safe in frontmatter because that code never ships to the client. The same call in browser-side JavaScript would expose the key to anyone who opens dev tools. - Fewer round trips for the visitor. One request from your server to the API, at build or request time, replaces what would otherwise be a request from every visitor's browser.
---
// Safe: this key never reaches the browser.
const response = await fetch("https://api.example.com/data", {
headers: { Authorization: `Bearer ${import.meta.env.API_SECRET_KEY}` },
});
const data = await response.json();
---Reading local files
Frontmatter can also read from the filesystem directly, which is useful for local data that doesn't warrant a full content collection:
---
import fs from "node:fs";
const raw = fs.readFileSync("./data/team.json", "utf-8");
const team = JSON.parse(raw);
---
<ul>
{team.map((member) => (
<li>{member.name}</li>
))}
</ul>Build time vs. request time
Exactly when this frontmatter code runs depends on the page's rendering mode, covered fully in a later lesson: in static output, it runs once at build time, and the result is baked into a static HTML file served to every visitor. In server output, it runs on each incoming request, so the fetched data can be different every time — useful for data that changes too often to bake into a build. Either way, the defining trait doesn't change: this code executes somewhere other than the visitor's browser, and only its output — rendered HTML — ever reaches them.