# base44 integration rules

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

base44 builds **React** SPAs (Vite + Tailwind), and a naive integration breaks in predictable ways: `AddressLookup.setup()` with CSS-selector `outputFields` fights React's controlled inputs, the agent guesses an outdated package version, and it assumes the API key can reach the browser through a Secret when the plan doesn't allow it. Follow every rule below to pre-empt them.

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

* Install `@addresszen/address-lookup` at the latest major: `@addresszen/address-lookup@2`. Do **not** guess a version number — older versions are out of date and the API differs.

## 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 `AddressLookup.watch` (**not** `setup`), once **both** the API key and the component are mounted, guarded by a `useRef` so it runs exactly once (React Strict Mode mounts effects twice):

  ```
  AddressLookup.watch({

    inputField: "#line_1",

    apiKey,

    onAddressRetrieved: (address) => {

      setForm({

        line_1: address.line_1,

        line_2: address.line_2,

        city: address.city,

        state: address.state,

        zip_plus_4_code: address.zip_plus_4_code,

        country: address.country,

      });

    },

  });
  ```

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

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

The AddressZen 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). How you supply it depends on the base44 plan:

* **Free plan (no Secrets / backend functions):** Secrets and backend functions require Builder+. Do **not** try to read the key from a Secret, a `VITE_` env var, or a backend function — none of those are available, and the autocomplete will silently never load. Instead, paste the `ak_…` key **directly into the component** as a constant. This is safe because it is a publishable key locked to your origin via Allowed URLs.
* **Builder+ plan:** read the key from the `ADDRESSZEN_API_KEY` Secret via a small backend function that returns it to the frontend, and fetch it on mount into state. A `VITE_`-prefixed env var also works if your build injects it at build time.

If you are unsure which plan is active, just paste the key directly — it always works and is safe for a publishable key.

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

* Pass `onSearchError` and `onSuggestionError` to `watch()`. If the AddressZen 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.addresszen.com/docs/address-lookup/react.md)
* [Address Lookup reference](https://docs.addresszen.com/docs/address-lookup.md)
