docs
    re-site-kit: Cards and grids
    v0.6.0.md

    Cards and grids

    ListingCard shows one listing as a card: photo, price, address, specs and the listing brokerage. ListingGrid lays a list of them out in a responsive grid. Both render on the server with no client JavaScript, so they work in search results, building pages, home page sections and anywhere else you have listings.

    import { ListingCard, ListingGrid } from '@sonordev/re-site-kit'

    ListingGrid

    <ListingGrid
      listings={listings}
      hrefFor={(listing) => (listing.slug ? `/listings/${listing.slug}` : null)}
      emptyState={<p>No homes match those filters.</p>}
    />
    PropTypeWhat it does
    listingsRealEstateListing[]The listings to show. Required.
    hrefFor(listing) => string | nullEach card's link. Return null for no link. Leave the prop out and cards link to the listing's source_url.
    emptyStateReactNodeRendered instead of the grid when listings is empty. Without it, an empty list renders nothing.
    classNamestringAdded next to re-listing-grid.

    The grid auto-fills columns at least 320px wide with a 1.5rem gap. Every card in it credits the listing brokerage (see Brokerage credit).

    Link to your own pages. Without hrefFor, cards link to source_url, the listing's page at its source, and open it in a new tab. On a site with a page per listing you'll almost always want hrefFor. Guard against listings without a slug so no card links to /listings/null:

    hrefFor={(listing) => (listing.slug ? `/listings/${listing.slug}` : null)}

    A different layout. The grid's column rule is an inline style. For a carousel, a two-column layout or anything else, map ListingCard inside your own container:

    <ul className="featured-homes">
      {listings.map((listing) => (
        <li key={listing.id}>
          <ListingCard listing={listing} href={listing.slug ? `/listings/${listing.slug}` : null} />
        </li>
      ))}
    </ul>

    ListingCard

    <ListingCard listing={listing} href="/listings/12-oak-st-springfield-123456" />
    PropTypeDefaultWhat it does
    listingRealEstateListingThe listing. Required.
    hrefstring | nullthe listing's source_urlWhere the card links. Pass null to render the card without a link.
    showAttributionbooleantrueShow "Courtesy of" the listing brokerage.
    classNamestringAdded next to re-listing-card.

    What's on the card:

    • The first photo, cropped to 16:10, lazy-loaded, with the street address as its alt text. With no photo, the frame keeps its shape on a gray background.
    • The price, from formatPrice (the feed's own display string when it has one).
    • A status badge, only when the listing isn't active (for example "pending").
    • The street address, the location and the specs ("3 bd · 2 ba · 1,960 sqft").
    • "Courtesy of Example Realty", the listing brokerage, when the listing has a listing_office_name and showAttribution is on.

    Links. An href that starts with http:// or https:// opens in a new tab with rel="noopener noreferrer". A path like /listings/12-oak-st opens in the same tab. So pass a path, not a full URL, for pages on your own site.

    Brokerage credit

    MLS IDX rules generally require the listing office to be named wherever another broker's listing is shown, and that includes search results, not just the detail page. It's also the common case: a page of search results usually shows listings from many brokerages, and only a few are your own.

    So ListingCard credits the listing brokerage by default, and ListingGrid always shows it. Style it with .re-listing-card__courtesy:

    .re-listing-card__courtesy {
      color: #4b5563;
      text-transform: uppercase;
      letter-spacing: 0.04em;
    }

    Its size, opacity and margin are inline styles, so changing those takes !important (see Styling and performance).

    Pass showAttribution={false} only where the surrounding UI already credits the office, for example in your own card layout that prints the brokerage elsewhere. More on what IDX asks for is in IDX compliance.

    Styling

    Both components ship a few inline layout essentials and a class on every element. The full list of classes, and how to override the inline styles, is in Styling and performance.