Recipes

Common page compositions that combine SDK fetchers and blocks into full pages. Copy, adapt, ship.


Listing Search Page

A full-page listings search with search box, grid, and filters. Uses the listing-grid and search-box blocks.

app/imoveis/page.tsx
import { ListingGrid } from "@/components/garagem/listing-grid";
import { SearchBox } from "@/components/garagem/search-box";

export default function ListingsPage({
  searchParams,
}: {
  searchParams: Promise<{ city?: string; type?: string }>;
}) {
  const params = await searchParams;

  return (
    <main className="mx-auto max-w-7xl px-4 py-8">
      <h1 className="text-2xl font-bold mb-6">Imóveis</h1>

      <div className="mb-8">
        <SearchBox
          siteId={process.env.GARAGEM_SITE_ID!}
          onSelect={(listing) => {
            window.location.href = `/imoveis/${listing.id}`;
          }}
        />
      </div>

      <ListingGrid
        siteId={process.env.GARAGEM_SITE_ID!}
        filters={{
          city: params.city,
          propertyType: params.type,
        }}
        limit={24}
        next={{ revalidate: 300 }}
      />
    </main>
  );
}

Listing Detail Page

Individual listing page with gallery, details, and contact form. Uses gallery-carousel and contact-form blocks.

app/imoveis/[id]/page.tsx
import { getListing } from "@garagem-ai/site-sdk";
import { GalleryCarousel } from "@/components/garagem/gallery-carousel";
import { ContactForm } from "@/components/garagem/contact-form";

export default async function ListingDetailPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const listing = await getListing({
    siteId: process.env.GARAGEM_SITE_ID!,
    listingId: id,
    next: { revalidate: 60 },
  });

  const price = listing.price?.toLocaleString("pt-BR", {
    style: "currency",
    currency: "BRL",
  });

  return (
    <main className="mx-auto max-w-5xl px-4 py-8">
      <GalleryCarousel media={listing.media} />

      <div className="mt-8 grid gap-8 lg:grid-cols-3">
        <div className="lg:col-span-2 space-y-4">
          <h1 className="text-2xl font-bold">{listing.title}</h1>
          <p className="text-xl font-semibold">{price ?? "Sob consulta"}</p>
          <p className="text-muted-foreground">{listing.description}</p>

          <div className="flex flex-wrap gap-2 mt-4">
            {listing.amenities.map((a) => (
              <span key={a} className="rounded-full bg-muted px-3 py-1 text-xs">
                {a}
              </span>
            ))}
          </div>
        </div>

        <aside>
          <ContactForm
            siteId={process.env.GARAGEM_SITE_ID!}
            listingId={listing.id}
            title="Tenho interesse"
            subtitle="Preencha e um corretor entrará em contato."
            metaPixelEvent="Lead"
          />
        </aside>
      </div>
    </main>
  );
}

Homepage with Featured Listings

Landing page showing featured listings and a CTA. Minimal setup.

app/page.tsx
import { getListings } from "@garagem-ai/site-sdk";
import { ListingCard } from "@/components/garagem/listing-card";
import { ContactForm } from "@/components/garagem/contact-form";

export default async function HomePage() {
  const { listings } = await getListings({
    siteId: process.env.GARAGEM_SITE_ID!,
    filters: { limit: 6 },
    next: { revalidate: 300 },
  });

  return (
    <main className="mx-auto max-w-7xl px-4 py-12">
      <section className="text-center mb-12">
        <h1 className="text-4xl font-bold">Encontre seu imóvel</h1>
        <p className="mt-2 text-lg text-muted-foreground">
          Os melhores imóveis da região, tudo em um lugar.
        </p>
      </section>

      <section className="mb-16">
        <h2 className="text-xl font-semibold mb-6">Destaques</h2>
        <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {listings.map((listing) => (
            <ListingCard
              key={listing.id}
              listing={listing}
              href={`/imoveis/${listing.id}`}
            />
          ))}
        </div>
      </section>

      <section className="mx-auto max-w-md">
        <ContactForm
          siteId={process.env.GARAGEM_SITE_ID!}
          title="Fale conosco"
          subtitle="Não encontrou o que procura? Conte pra gente."
        />
      </section>
    </main>
  );
}