# Wix

> Embed an HTML form in a Wix site that posts with fetch, because Wix runs custom HTML in a sandboxed frame that can't navigate the page.

Wix Forms keep submissions in Wix and have no setting for an outside action URL. To use Ikiform, put your own form in an **Embed HTML** element. Wix runs that code inside a sandboxed iframe served from a separate `filesusr.com` host, and that shapes everything below.

## Why the form posts with fetch

- Wix's frame allows forms, scripts, popups and same-origin access, but not top-level navigation. `target="_top"` can't move the visitor to a thank-you page.
- A normal form post would load the result inside the small frame, and Ikiform's thank-you, error and check pages refuse to render inside frames. Visitors would see an empty or refused frame.
- Posting with `fetch` and `Accept: application/json` keeps the visitor in place and shows the result inside the embed.

## Add the embed

### Step 1: Add an Embed HTML element

In the Wix Editor, open **Add Elements** > **Embed Code** > **Embed HTML**. In Wix Studio it's under **Embed & Social**. Choose **Code** rather than **Website address**.

### Step 2: Paste the form

`Embed HTML`:

```html
<form id="contact" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post" novalidate>
  <label>Email <input name="email" type="email" autocomplete="email" required /></label>
  <p class="error" data-error-for="email"></p>
  <label>Message <textarea name="message" rows="4"></textarea></label>
  <p class="error" data-error-for="message"></p>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
  <p role="status" data-status></p>
</form>

<script>
  const form = document.querySelector("#contact");
  const button = form.querySelector("button");
  const status = form.querySelector("[data-status]");

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    button.disabled = true;
    status.textContent = "Sending";
    try {
      const response = await fetch(form.action, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(form),
      });
      const result = await response.json();
      const fields = result.ok ? {} : result.error.fields ?? {};
      for (const slot of form.querySelectorAll("[data-error-for]")) {
        slot.textContent = fields[slot.dataset.errorFor]?.[0] ?? "";
      }
      status.textContent = result.ok ? "Thanks, we got it." : result.error.message;
      if (result.ok) form.reset();
    } catch {
      status.textContent = "Couldn't reach the server. Try again.";
    } finally {
      button.disabled = false;
    }
  });
</script>

<style>
  body { margin: 0; font: 15px/1.5 system-ui, sans-serif; }
  form { display: grid; gap: 8px; }
  label { display: grid; gap: 4px; }
  input, textarea, button { font: inherit; padding: 8px 10px; border: 1px solid #d4d4d4; border-radius: 6px; }
  button { background: #111; color: #fff; border: 0; }
  .error { margin: 0; color: #b42318; font-size: 13px; }
</style>
```

The frame doesn't inherit your site's fonts or colors, so the `<style>` block sets them. Change it to match your site.

### Step 3: Size and publish

Drag the element tall enough to fit the field errors and the status line, then publish. The editor only shows a placeholder for custom code, so test on the published site.

> Wix's site-wide **Custom Code** setting, which needs a premium plan and a connected domain, adds code to the page head or body. It can't place a form at a spot in your layout, so the Embed HTML element is still the place for the form itself.

## Spam settings

Leave the form's spam check off. Ikiform's human check needs either its check page or a small ikiform.com frame, and neither can run from inside Wix's sandbox. The honeypot and spam scoring still run on every post.

## Allowed domains

Ikiform sees the frame's host, not your Wix domain, because the post is sent from inside the iframe. To find it, open your published page, inspect the embed and read the iframe's `src`.

| Where the form runs | Add to Allowed domains |
| --- | --- |
| The embed frame | The exact host from the iframe's `src`, such as `www-example-com.filesusr.com` |
| Your Wix or custom domain | Not needed for the embed, since the post never comes from it |

- Never add `filesusr.com` on its own. Every site on that host would match, including other people's.
- The frame host is derived from your site address, so it changes if you connect a new domain. Check it again after a domain change.

## Test checklist

1. Publish and open the live page in a private window.
2. With [fields](https://ikiform.com/docs/fields.md) defined, submit with an empty email. The form has `novalidate`, so Ikiform's message should appear under the field.
3. Submit a real message and confirm the status line reads "Thanks, we got it." and the submission is listed in Ikiform.
4. Turn on allowed domains with the frame host and submit again. It should still land under received.

## Troubleshooting

| What you see | Why | Fix |
| --- | --- | --- |
| The embed goes blank or shows a refused page after submit | The script didn't run, so the browser posted normally and tried to load Ikiform's page inside the frame | Check the script made it into the element, and that the form id is `contact`. |
| Submissions arrive under spam with `origin_mismatch` | The frame host isn't in allowed domains, or you listed your Wix domain instead | Add the exact `filesusr.com` host from the iframe's `src`. |
| The status line says `Complete the human check, then send the form again.` | The spam check is set to **Ikiform**, which can't run inside the sandbox | Turn the check off for this form. |
| The status line says it couldn't reach the server | An ad blocker or network filter blocked the request. Some networks block `filesusr.com` itself. | Test on another network or in a private window without extensions. |
| Error messages are cut off | The element is shorter than the form | Make the element taller in the editor. |

## Related

- [Plain JavaScript](https://ikiform.com/docs/guides/javascript.md): `fetch` with inline errors.
- [Site builders](https://ikiform.com/docs/guides/site-builders.md): The embed that works in any builder.
- [Allowed domains](https://ikiform.com/docs/spam.md#allowed-domains): How origins are matched, and what a mismatch does.
- [Spam protection](https://ikiform.com/docs/spam.md): Honeypot, scoring and the human check.
