# Leap.new integration rules

This page is the detailed rule set referenced by the [Leap.new starter prompt](/docs/integrations/leap-new.md). It exists so the prompt you paste into the builder can stay short — the agent fetches this page and follows the rules below. It is intentionally kept out of the sidebar.

Leap.new builds **Encore.ts** apps with a **React** frontend, so use the [`@ideal-postcodes/react`](https://docs.ideal-postcodes.co.uk/docs/address-finder/react.md) component. It renders its own `<input>`, attaches the widget, and hands the selected address back through `onAddressRetrieved` — no `useEffect`, `useRef`, CSS selectors, or controlled/uncontrolled juggling. The one thing left to get right is delivering the API key from an Encore secret to the browser. The rules below cover both.

## Install the package[​](#install-the-package "Direct link to Install the package")

* Install `@ideal-postcodes/react` in the frontend. It pulls in `@ideal-postcodes/address-finder` automatically; you don't need to add it separately.

## Wiring the widget[​](#wiring-the-widget "Direct link to Wiring the widget")

* Render the component and read the resolved address from `onAddressRetrieved`:

  ```
  import { AddressFinder } from "@ideal-postcodes/react";



  <AddressFinder

    apiKey={apiKey}

    onAddressRetrieved={(address) => {

      setForm({

        line_1: address.line_1,

        line_2: address.line_2,

        line_3: address.line_3,

        post_town: address.post_town,

        postcode: address.postcode,

        country: address.country,

      });

    }}

  />
  ```

* The component draws its own search `<input>`. To reuse a styled input, pass it as a single child instead (wrap mode): `<AddressFinder apiKey={apiKey} ...><input className="..." /></AddressFinder>`.

* Populate every **other** field from the `onAddressRetrieved` callback into React state. Those fields are controlled (`value` + `onChange`) as normal — the component never writes to them directly.

* After populating fields from the callback, trigger their validation (e.g. set state, then run your existing validators) so a selected address clears any "required" errors.

* The stylesheet is auto-injected by default (`injectStyle` defaults to `true`), so the dropdown is styled out of the box. No separate CSS import is required.

## API key[​](#api-key "Direct link to API key")

The Ideal Postcodes API key is a **publishable** browser key — it is designed to ship in client-side code and is protected by [Allowed URLs](/docs/guides/allowed-urls.md), not by secrecy (exactly like a Stripe `pk_` publishable key). The component runs in the browser, so the key has to reach client code:

* Add a tiny Encore endpoint that reads `secret("IDEAL_POSTCODES_API_KEY")` and returns it to the frontend, e.g. `GET /config/ideal-postcodes-api-key`.
* Fetch that endpoint **once on mount** and store the key in state. Render `<AddressFinder>` only once the key has arrived (render nothing, or a disabled input, until then) and pass it as the `apiKey` prop.
* Do **not** hardcode the key in the component.

## Errors[​](#errors "Direct link to Errors")

* Pass `onSearchError` and `onSuggestionError` props. If the Ideal Postcodes API returns a 4xx error, surface the error's message to the user — do not swallow it. A `401` almost always means the app's origin is missing from the key's Allowed URLs.

## Reference docs[​](#reference-docs "Direct link to Reference docs")

* [React integration guide](https://docs.ideal-postcodes.co.uk/docs/address-finder/react.md)
* [Address Finder reference](https://docs.ideal-postcodes.co.uk/docs/address-finder.md)
