# The search island

> The floating dates-and-guests island for custom sites — the search-island contract, useSearchIsland, the collapsed summary pill, scope-aware availability, keyboard-enterable labelled fields, and where the results rules live.




Set the stay's dates and guests once. The `search-island` contract is the
floating island custom sites share with the Elements ActionBar: it collapses
into an `Add dates · 2 guests` pill on a phone, docks as a bar on a desktop,
carries the stay in [the canonical stay URL](https://developers.kismet.travel/sdk/stay-query.md), and once dated
it re-runs availability for whatever page it is on.

## Take it, restyle it

The behavior ships as one headless hook from `@kismet-tech/sdk/react`. You
render everything — pill, fields, sheet, bar — and the hook owns the stay
state, the summary line, and the scoped availability request:

```tsx
'use client';
import { useSearchIsland } from '@kismet-tech/sdk/react';

function GroupIsland({ groupSlug }: { groupSlug: string }) {
  const island = useSearchIsland({
    scope: { kind: 'group', groupSlug },
  });

  if (!island.isEditorOpen) {
    return (
      <button data-kismet-island-pill aria-expanded="false" onClick={island.openEditor}>
        {island.summaryLabel}
      </button>
    );
  }
  return (
    <form data-island-editor onSubmit={(event) => event.preventDefault()}>
      <label htmlFor="check-in">Check-in</label>
      <input id="check-in" type="date" onChange={(event) =>
        island.applyStaySettings({ checkIn: event.target.value || undefined })} />
      {/* check-out and guests likewise */}
    </form>
  );
}
```

- `summaryLabel` is the collapsed line — `Add dates · 2 guests` undated,
  `12–15 Oct · 4 guests` dated — produced by
  `formatStaySelectionSummary`, so every site reads the same.
- `applyStaySettings({ checkIn, checkOut, guests })` writes the canonical
  stay URL through `useStayQuery`. Dates travel as a pair; a reload, a
  shared link, or the move to a property page keeps them.
- `availabilityRequest` is `undefined` while undated, then
  `{ checkIn, checkOut, guests, group? }` — the exact options for
  `vacationRental.availability.get`. The `group` key is present only when
  the scope is a group, so a group page re-runs availability for that group
  and a results page re-runs it for the current set.

## Scope

Pass the scope the page is on:

| Page | Scope | Availability request |
| --- | --- | --- |
| Group landing | `{ kind: 'group', groupSlug }` | carries `group: groupSlug` |
| Results | `{ kind: 'results' }` | no `group` key |

## What changes once dates are set

When the stay is dated, cards show the **before-tax stay total** from
`vacationRental.availability.get` — `totalBeforeTaxMinor` per vacation
rental — never a final quote. Confirmed-unavailable vacation rentals are
marked (`Not available for your dates`) or sink to the bottom of the list
through [`sortVacationRentals`](https://developers.kismet.travel/sdk/stay-query.md); a rental whose
availability is **unknown** stays in place and is never treated as
unavailable. Those rendered-results rules are owned by the
[browse channel](https://developers.kismet.travel/sdk/browse-channel.md), property-card, and results-page
contracts — the island contract governs where the stay lives, not how
results react.

## The contract

`search-island@1` builds on the `stay-query` contract. State ownership is
the MUST, enforced statically on every site that runs
`kismet recipes check` — both starters do — and on rendered pages by the
two must-severity criteria of the `search-island` recipe scenario:

| Requirement | Level | Check |
| --- | --- | --- |
| Stay settings live in the canonical stay URL (static) | MUST | `recipes/stay-query-source` |
| Never hold the stay dates or guest count in local component state | MUST NOT | `recipes/search-island-source` |
| Applied settings reach the canonical URL (rendered) | MUST | `conformance/search-island-stay-url` |
| Stay survives a reload (rendered) | MUST | `conformance/search-island-persistence` |
| Collapsed summary pill reflects the observed stay | SHOULD | `conformance/search-island-pill` |
| Fields labelled and keyboard-enterable | SHOULD | `conformance/search-island-fields` |
| Availability rebinds to the active scope | SHOULD | `conformance/search-island-scope` |

The recipe scenario grades a page that declares `search-island`: the runner
fills the canonical stay form with the keyboard, reads the URL keys the
page writes, reloads, and correlates the availability requests in that
window. The pill SHOULD is scoped to pages that render the optional
`data-kismet-island-pill` hook. A MUST miss fails the site; a SHOULD miss
warns and never blocks a declaration.

Placement is free space: dock the bar, float the pill, restyle the sheet.
