> ## Documentation Index
> Fetch the complete documentation index at: https://developers.tally.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Code injection

> Run custom JavaScript inside a Tally form hosted on your own custom domain.

When you serve a form via your own [custom domain](https://tally.so/help/custom-domains),
you can inject custom JavaScript that runs **inside the form page itself** — no embed or
popup involved. Use it to prefill fields from an API call, read values entered by the
respondent, or read previously saved answers from the browser.

Paste your code into the **Code injection** box of your custom domain settings.

## Wait for the DOM

Wrap your code in a `DOMContentLoaded` event handler so the form has finished rendering
before you query its inputs.

```javascript theme={null}
document.addEventListener('DOMContentLoaded', function () {
  // Your code goes here
  // ...
});
```

## Read an input element

Each block in a Tally form has a stable UUID — you can find it by inspecting the rendered
form in your browser's DevTools. Pass that UUID to `document.getElementById()` to get the
underlying input element.

```javascript theme={null}
document.addEventListener('DOMContentLoaded', function () {
  // You can find the input's ID by inspecting your form
  const emailInput = document.getElementById('c1cbc8e4-b2f3-4e63-a683-ec9eadbcb022');
});
```

## Set a value on an input element

The form is a React app, so simply assigning to `.value` won't update its state — React's
synthetic event system needs to see a real input event. The snippet below uses the native
setter and dispatches a bubbling `input` event so React picks the change up.

```javascript theme={null}
document.addEventListener('DOMContentLoaded', async function () {
  // We get the input element
  const emailInput = document.getElementById('c1cbc8e4-b2f3-4e63-a683-ec9eadbcb022');

  // Get the email that was typed in
  const email = emailInput.value;

  // Do an API lookup using the email to get the user's unique identifier
  const response = await fetch(`https://api.example.com/getUserId?email=${email}`);
  const { userId } = await response.json();

  // Get the target input and set its value
  const userIdInput = document.getElementById('184b69ee-0c9f-449a-b361-b5250ccd2cb3');

  // This is necessary to bubble the event up to the input and update the React app state
  const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
    window.HTMLInputElement.prototype,
    'value',
  ).set;
  nativeInputValueSetter.call(userIdInput, userId);
  userIdInput.dispatchEvent(new Event('input', { bubbles: true }));
});
```

## Read previously entered answers

Tally persists in-progress answers to the browser's `localStorage` under a key derived from
the form ID. You can read them out at any time:

```javascript theme={null}
// You can find the form ID in the URL of the form pages
// Example: https://tally.so/forms/mRoDv3/share
const formId = 'mRoDv3';
let data = localStorage.getItem(`FORM_DATA_${formId}`);
if (data) {
  data = JSON.parse(data);
}
```

## Events

Code injection has access to the same JavaScript events as embeds and popups, dispatched
as `CustomEvent`s on `window` rather than as `postMessage` payloads. See
[Events](/widgets/events) for the full payload reference and listener patterns.
