Skip to main content

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.

When you serve a form via your own custom domain, 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.
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.
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.
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:
// 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 CustomEvents on window rather than as postMessage payloads. See Events for the full payload reference and listener patterns.