# Anything integration rules

This page is the detailed rule set referenced by the [Anything starter prompt](/docs/integrations/anything.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.

Anything builds **React** apps, and a naive integration breaks in predictable ways: a `<script>` tag written in JSX never executes (so the widget never loads), CSS-selector autofill (`outputFields`) silently loses to React's controlled inputs, and setup runs before the script or API key is ready. Follow every rule below to pre-empt them.

## Loading the library[​](#loading-the-library "Direct link to Loading the library")

* Do **not** put a `<script>` tag in the JSX/returned markup. React does not execute script tags rendered that way, so the widget never loads and `window.IdealPostcodes` stays undefined.

* Load it inside a `useEffect` by creating the element yourself, and track readiness in state:

  ```
  useEffect(() => {

    const s = document.createElement("script");

    s.src = "https://cdn.jsdelivr.net/npm/@ideal-postcodes/address-finder-bundled@5";

    s.async = true;

    s.onload = () => setScriptReady(true);

    document.body.appendChild(s);

  }, []);
  ```

  The global is `window.IdealPostcodes.AddressFinder`.

* If, and only if, your build can resolve npm packages, you may instead `import { AddressFinder } from "@ideal-postcodes/address-finder-bundled"` and skip the script loader. Do not invent any other package name.

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

* The search input (address line 1) **must be uncontrolled**: give it `id="line_1"` but do **not** put `value=...` or `onChange=...` on it. The widget reads and writes this field directly; binding React state to it fights the library and breaks the dropdown.

* Initialise with `AddressFinder.watch` (not `setup`), once **both** the API key and the script are ready, guarded by a `useRef` so it runs exactly once:

  ```
  AddressFinder.watch({

    inputField: "#line_1",

    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,

      });

    },

  });
  ```

* Populate every **other** field from the `onAddressRetrieved` callback into React state. Those fields **are** controlled (`value` + `onChange`). Do **not** use `outputFields` / CSS-selector autofill for them — React overwrites direct DOM writes.

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

* Add a backend function at `web/api/idpc-config/route.ts` that reads the `IDEAL_POSTCODES_API_KEY` Saved Secret and returns it to the frontend. Fetch it on mount into state. Do not hardcode the key.

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

* Pass `onSearchError` and `onSuggestionError` to `watch()`. If the Ideal Postcodes API returns a 4xx error, surface the error's message to the user — do not swallow it.

## 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)
