Polling

data-init fetches once on load and data-on-interval__duration.5s re-fetches every five seconds. The endpoint renders the same <ServerTime/> component the page used for its loading state; the morph swaps it in place.

Server clock

Loading server time…

Re-fetch by convention

data-on:* listens for any DOM event, so re-fetching is a convention rather than a plugin: give a region data-on:refetch="@get(…)" and dispatch a bubbling CustomEvent('refetch') at it from anywhere.

<div data-on:refetch="@get('/api/time')">
  <button data-on:click="evt.target.dispatchEvent(new CustomEvent('refetch', {bubbles: true}))">
    Refresh now
  </button>
</div>

The shared component

src/components/ServerTime.astro
---
/** Server-time fragment shared by the polling page and /api/time. */
interface Props {
  time?: string;
}

const { time } = Astro.props;
---

{
  time ? (
    <div id="server-time">
      Server time: <strong>{time}</strong>
    </div>
  ) : (
    <div id="server-time" class="hint">
      Loading server time…
    </div>
  )
}

The endpoint

src/pages/api/time.ts
import { html } from '@wrux/astro-datastar/server';
import type { APIRoute } from 'astro';
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import ServerTime from '../../components/ServerTime.astro';

export const prerender = false;

export const GET: APIRoute = async () => {
  const container = await AstroContainer.create();
  return html(
    await container.renderToString(ServerTime, {
      props: { time: new Date().toLocaleTimeString('en-GB') },
    }),
  );
};