← Files WixARCHIVED FILE
skills/wix-headless-templates/rentals/project/src/pages/rentals/[slug].astro
3.09 KB · Oct 8, 2026 · 12:02 UTC
---
// Rental detail + booking 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. A
// rental is a Bookings service, so the Bookings service-page metadata applies. Keep all three SEO
// pieces and this frontmatter; swap the island import to YOUR rental component. The island is
// client:only — availability is time-zone/session-specific.
import SiteLayout from "../../layouts/SiteLayout.astro";
import RentalBookingView from "../../components/rentals/RentalBookingView";
import { fetchRentalBySlug } from "../../wix/rentals/rentals";
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.bookings.id,
pageIdentifier: WIX_APPS.bookings.servicePageMetadata.pageIdentifier,
identifiers: {
slug: WIX_APPS.bookings.servicePageMetadata.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 rental = null;
let seoTagsServiceConfig = null;
try {
[rental, seoTagsServiceConfig] = await Promise.all([
fetchRentalBySlug(slug),
loadSEOTagsServiceConfig({
pageUrl,
itemType: seoTags.ItemType.BOOKINGS_SERVICE,
itemData: { slug },
}).catch(() => null),
]);
} catch {
// Guarded: an unhandled SSR throw truncates the response mid-stream.
}
if (!rental) {
return new Response(null, { status: 404 });
}
---
<SiteLayout title={rental.name}>
<SEO.Tags seoTagsServiceConfig={seoTagsServiceConfig} slot="seo-tags" />
<div class="grid gap-10 md:grid-cols-2">
<div>
{rental.imageUrl && (
<div class="aspect-[4/3] overflow-hidden rounded-xl bg-secondary">
<img src={rental.imageUrl} alt={rental.name} class="h-full w-full object-cover" />
</div>
)}
<h1 class="mt-5 text-2xl font-semibold tracking-tight">{rental.name}</h1>
{rental.tagLine && <p class="mt-1 text-muted-foreground">{rental.tagLine}</p>}
<p class="mt-2 text-sm text-muted-foreground">
{rental.rateLabel && <span class="text-foreground">{rental.rateLabel}</span>}
{rental.rateLabel && rental.rangeLabel ? " · " : ""}
{rental.rangeLabel}
</p>
{rental.locations.some((l) => l.name) && (
<p class="mt-1 text-sm text-muted-foreground">{rental.locations.map((l) => l.name).filter(Boolean).join(" · ")}</p>
)}
{rental.description && <p class="mt-4 text-sm leading-relaxed text-muted-foreground">{rental.description}</p>}
</div>
<div>
<RentalBookingView client:only="react" rental={rental} />
</div>
</div>
</SiteLayout>
SHA-256: 7af12fbf1627ab97bac19a94785115fa812dd21bf0bd46456717799b1530d1ef