← Files WixARCHIVED FILE

skills/wix-headless-templates/events/project/src/pages/events/[slug].astro

3.91 KB · Oct 8, 2026 · 12:02 UTC

↓ Download file

See the change to this file →

---
// 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