← Files WixARCHIVED FILE
skills/wix-headless-templates/events/project/src/pages/events/[slug].astro
3.91 KB · Oct 8, 2026 · 12:02 UTC
---
// Event detail + registration page.
//
// This is a Wix ITEM PAGE: the wixMetadata export + <SEO.Tags> below let the site owner edit
// this page's title/description/OG in the dashboard and register the route in the sitemap.
// Keep all three SEO pieces and this frontmatter; swap the island import to YOUR registration
// component. The registration island is client:only — it runs visitor-session calls and
// redirects. A recurring event lists its other dates (the same series, fetched here).
import SiteLayout from "../../layouts/SiteLayout.astro";
import EventRegistrationView from "../../components/events/EventRegistrationView";
import { fetchEventBySlug, fetchOccurrences } from "../../wix/events/events";
import type { EventSummary } from "../../wix/events/types";
import { WIX_APPS } from "@wix/essentials";
import { SEO } from "@wix/seo/components";
import { loadSEOTagsServiceConfig } from "@wix/seo/services";
import { seoTags } from "@wix/seo";
export const wixMetadata = {
appDefId: WIX_APPS.events.id,
pageIdentifier: WIX_APPS.events.eventPageMetadata.pageIdentifier,
identifiers: {
slug: WIX_APPS.events.eventPageMetadata.identifiers.slug,
},
};
const slug = Astro.params.slug!;
// Behind Wix's proxy the request URL is internal — the real public page URL arrives on
// x-wix-forwarded-url; the SEO service needs the public one.
const forwardedUrl = Astro.request.headers.get("x-wix-forwarded-url");
const pageUrl =
forwardedUrl && URL.canParse(forwardedUrl) && /^https?:$/.test(new URL(forwardedUrl).protocol)
? forwardedUrl
: Astro.url.href;
let event = null;
let seoTagsServiceConfig = null;
try {
[event, seoTagsServiceConfig] = await Promise.all([
fetchEventBySlug(slug),
loadSEOTagsServiceConfig({
pageUrl,
itemType: seoTags.ItemType.EVENTS_PAGE,
itemData: { slug },
}).catch(() => null),
]);
} catch {
// Guarded: an unhandled SSR throw truncates the response mid-stream.
}
if (!event) {
return new Response(null, { status: 404 });
}
// Other dates of a recurring series (empty for a one-time event); guarded like every SSR read.
let occurrences: EventSummary[] = [];
if (event.recurringCategoryId) {
try {
occurrences = (await fetchOccurrences(event.recurringCategoryId)).filter((o) => o.id !== event.id);
} catch {
/* the page renders without the date list */
}
}
---
<SiteLayout title={event.title}>
<SEO.Tags seoTagsServiceConfig={seoTagsServiceConfig} slot="seo-tags" />
<div class="grid gap-10 md:grid-cols-2">
<div>
{event.imageUrl && (
<div class="aspect-[3/2] overflow-hidden rounded-xl bg-secondary">
<img src={event.imageUrl} alt={event.title} class="h-full w-full object-cover" />
</div>
)}
{event.dateLabel && <p class="eyebrow mt-5">{event.dateLabel}</p>}
<h1 class="mt-1 text-2xl font-semibold tracking-tight">{event.title}</h1>
<p class="mt-1 text-sm text-muted-foreground">
{event.locationType === "ONLINE" ? "Online event" : event.locationName}
{event.priceLabel ? ` · ${event.priceLabel}` : ""}
</p>
{event.address && <p class="mt-0.5 text-sm text-muted-foreground">{event.address}</p>}
{event.shortDescription && <p class="mt-4 text-sm leading-relaxed text-muted-foreground">{event.shortDescription}</p>}
{event.aboutParagraphs.map((p) => (
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">{p}</p>
))}
{occurrences.length > 0 && (
<div class="mt-6">
<p class="eyebrow">Other dates</p>
<ul class="mt-2 space-y-1">
{occurrences.map((o) => (
<li>
<a href={`/events/${encodeURIComponent(o.slug)}`} class="text-sm text-foreground underline">{o.dateLabel || o.title}</a>
</li>
))}
</ul>
</div>
)}
</div>
<div>
<EventRegistrationView client:only="react" event={event} />
</div>
</div>
</SiteLayout>
SHA-256: 0d9cd0ad7d49da550a10f8811cbc8532ec6092948884baa71771ed53c9a6c222