I have a page with dynamic reports. You can't prerender them all during the build, so the report component can be an SSR island. But the header and footer - they are static and i want them to be prerendered. How do i create such a page? How do i make it like prerender = true and the file name [...id].astro or something? For frontend i use React.
#Static dynamic reports
19 messages · Page 1 of 1 (latest)
Hi @wooden cargo. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Great use case! Here's how to approach this in Astro:
The Pattern: Static Shell + Server Island for Dynamic Content
You want a page where:
- The route itself is prerendered (static header/footer)
- The dynamic report component renders on-demand per request
This is exactly what Server Islands are designed for. [Server islands]
1. Set up the page route
Since your reports are dynamic (e.g., /reports/123), use a catch-all or parameterized route. With output: 'static' (the default), you can prerender the page shell:
---
// src/pages/reports/[...id].astro
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import ReportContent from '../components/ReportContent.astro';
const { id } = Astro.params;
---
<html>
<body>
<Header /> <!-- static, prerendered -->
<ReportContent id={id} server:defer>
<!-- Fallback shown while the island loads -->
<p slot="fallback">Loading report...</p>
</ReportContent>
<Footer /> <!-- static, prerendered -->
</body>
</html>
Note: With
[...id].astroandprerender = truein static mode, you'd normally needgetStaticPaths()to enumerate all IDs at build time. If you truly can't enumerate them, you'll need a different approach (see below).
2. The dynamic report Server Island
---
// src/components/ReportContent.astro
const { id } = Astro.props;
const data = await fetch(`<https://your-api.com/reports/${id}`>).then(r => r.json());
---
<div>
<!-- render your report data -->
<h2>{data.title}</h2>
</div>
Add server:defer to this component on the page to make it a server island. [Server islands]
3. Your React interactive components
For any interactive React parts inside the report, use client:* directives as usual: [Client Islands]
<MyReactChart client:load data={data} />
Important Caveat: Static vs. On-Demand Routes
-
If you can enumerate report IDs at build time, use
getStaticPaths()withprerender = true(default in static mode). The page shell is fully prerendered, and theserver:deferisland fetches dynamic data at request time. [getStaticPaths] -
If you cannot enumerate IDs, you need
output: 'server'withexport const prerender = false(or just omit it in server mode) for the route itself, and useserver:deferfor the dynamic parts. The static header/footer will still be rendered efficiently, but the route won't be fully prerendered. [On-demand rendering]
You cannot have a truly prerendered [...id].astro page for arbitrary unknown IDs — prerendering requires knowing the paths at build time. Server islands solve the component-level dynamic problem, but the route itself still needs to be either statically known or server-rendered on demand.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@wooden cargo Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@winged cave can i make it just "/report/index.astro" and it's prerendered and the component just starts loading once the it's hydrated and window object is available and from the window it can get the value of the id from the URL and then start fetching from the client?
@winged cave can i make it just "/report/index.astro" and it's prerendered and the component just starts loading once the it's hydrated and window object is available and from the window it can get the value of the id from the URL and then start fetching from the client?
Let me check the knowledge base for you again. One moment! :books:
Yes, that approach works well! You'd have a fully prerendered static page at /report/index.astro, and a React component that runs only on the client, reads the ID from window.location, and fetches the report data itself.
Use client:only="react" for your report component — this skips server rendering entirely and renders only in the browser, where window is available. [client:only directive]
---
// src/pages/report/index.astro
import Header from '../../components/Header.astro';
import Footer from '../../components/Footer.astro';
import ReportViewer from '../../components/ReportViewer.jsx';
---
<html>
<body>
<Header />
<ReportViewer client:only="react">
<div slot="fallback">Loading report...</div>
</ReportViewer>
<Footer />
</body>
</html>
Then in your React component, read the ID from the URL and fetch:
// src/components/ReportViewer.jsx
import { useState, useEffect } from 'react';
export default function ReportViewer() {
const [data, setData] = useState(null);
useEffect(() => {
const id = new URLSearchParams(window.location.search).get('id');
// or: window.location.pathname.split('/').pop()
fetch(`<https://your-api.com/reports/${id}`>)
.then(r => r.json())
.then(setData);
}, []);
if (!data) return <p>Loading...</p>;
return <h2>{data.title}</h2>;
}
Key points:
- The page (
index.astro) is fully prerendered — header and footer are static HTML. [Client Islands] client:only="react"means Astro skips server rendering of the component entirely, so there's nowindowaccess issue on the server. [client:only directive]- You can also show fallback content while the component loads using
slot="fallback".
This is a clean and simple solution if you don't need the report content to be crawlable or server-rendered.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@wooden cargo Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@winged cave my folder structure is "/pages/report/index.astro" but in the console i get "/report/MzjRX92ram 2ms
19:25:24 [WARN] [router] A getStaticPaths() route pattern was matched, but no matching static path was found for requested path /report/MzjRX92ram.
Possible dynamic routes being matched: src/pages/[...slug].astro."
Let me check the knowledge base for you again. One moment! :books:
The issue is that you have a src/pages/[...slug].astro catch-all route in your project, and when someone navigates to /report/MzjRX92ram, Astro is matching that dynamic route instead of your /report/index.astro page.
This is expected behavior based on Astro's route priority rules: routes with more path segments take precedence, but your [...slug].astro at the root is catching paths it shouldn't. [Route Priority Order]
The core problem is that your URL contains the ID as a path segment (/report/MzjRX92ram), not as a query parameter. Your index.astro only handles /report/ exactly.
Fix: Use a Query Parameter Instead
Change your app to pass the ID as a query parameter instead of a path segment:
/report?id=MzjRX92ram
Then in your React component, read it like this:
useEffect(() => {
const id = new URLSearchParams(window.location.search).get('id');
fetch(`<https://your-api.com/reports/${id}`>)
.then(r => r.json())
.then(setData);
}, []);
This way, /report/index.astro handles the route /report/ cleanly, and the ID is just a query string that your client-side React component reads — no routing conflict with [...slug].astro.
Alternative Fix: Rename to [...slug].astro Under /report/
If you want path-based IDs like /report/MzjRX92ram, you could rename your file to src/pages/report/[...slug].astro. But since you want a fully prerendered static page, you'd need getStaticPaths() to enumerate all IDs at build time, which you said isn't possible. [getStaticPaths required]
The query parameter approach is the simplest solution for your use case of a prerendered shell with client-side data fetching.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@wooden cargo Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@winged cave in terms of SEO or other browser related things there are no differences between a path usage for report id and query string?
Let me check the knowledge base for you again. One moment! :books:
This question is about general web/SEO concepts rather than Astro specifically, so it falls outside what I'm designed to answer in depth. I'm tuned to answer queries about Astro.
What I can tell you from the Astro knowledge sources is one relevant technical difference in the Astro context:
On prerendered pages,
request.urldoes not contain search parameters, like?type=new, as it's not possible to determine them ahead of time during static builds. However,request.urldoes contain search parameters for pages rendered on-demand. [request]
This means that for your prerendered /report/index.astro page, Astro itself cannot see or act on query parameters server-side — which is fine for your use case since you're reading them client-side in React anyway.