#Static dynamic reports

19 messages · Page 1 of 1 (latest)

wooden cargo
#

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.

winged caveBOT
#

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].astro and prerender = true in static mode, you'd normally need getStaticPaths() 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() with prerender = true (default in static mode). The page shell is fully prerendered, and the server:defer island fetches dynamic data at request time. [getStaticPaths]

  • If you cannot enumerate IDs, you need output: 'server' with export const prerender = false (or just omit it in server mode) for the route itself, and use server:defer for 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.

wooden cargo
#

@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?

winged caveBOT
#

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 no window access 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.

wooden cargo
#

@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."

winged caveBOT
#

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.

wooden cargo
#

@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?

winged caveBOT
#

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.url does not contain search parameters, like ?type=new, as it's not possible to determine them ahead of time during static builds. However, request.url does 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.

For the broader SEO and browser behavior question (crawlability, canonical URLs, indexing of query strings vs. path segments, etc.), I'd recommend consulting an SEO-focused resource, as that's beyond the scope of what I can reliably answer here.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: