# Eleventy

> An Eleventy shortcode that renders an Ikiform form in Nunjucks, Liquid and Markdown templates, with the form id kept in global data.

Eleventy outputs static HTML, so the form posts without any client JavaScript. A shortcode keeps the markup in one place and works the same in Nunjucks, Liquid and Markdown files.

## Set it up

### Step 1: Add the shortcode

`eleventy.config.js`:

```js
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);

export default function (eleventyConfig) {
  eleventyConfig.addShortcode("ikiform", (formId, thanks = "", subject = "") =>
    [
      `<form class="ikiform" action="https://ikiform.com/f/${encodeURIComponent(formId)}" method="post">`,
      '<label>Email <input name="email" type="email" autocomplete="email" required></label>',
      '<label>Message <textarea name="message" rows="5"></textarea></label>',
      thanks && `<input type="hidden" name="_redirect" value="${escape(thanks)}">`,
      subject && `<input type="hidden" name="_subject" value="${escape(subject)}">`,
      '<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>',
      '<button type="submit">Send</button>',
      "</form>",
    ]
      .filter(Boolean)
      .join("\n"),
  );
}
```

- The lines are joined without blank lines on purpose. In Markdown, a blank line ends an HTML block, and the rest of the form would be treated as text.
- This uses ES module syntax. If your `package.json` doesn't have `"type": "module"`, name the file `eleventy.config.mjs`.

### Step 2: Keep the form id in global data

`_data/ikiform.js`:

```js
export default {
  formId: process.env.IKIFORM_FORM_ID ?? "frm_xxxxxxxxxxxxxxxx",
  thanks: "https://example.com/thanks/",
};
```

Global data is available in every template as `ikiform`. The thank-you URL must be absolute and `https:`, because Ikiform ignores other redirects.

### Step 3: Use it

`contact.md`:

```text
---
title: Contact
---

Questions about pricing? Write to us.

{% ikiform ikiform.formId, ikiform.thanks, "Pricing question" %}
```

Markdown files are processed with Liquid by default, so the tag works there as written. Keep it on its own line and don't indent it by four spaces, or Markdown renders it as a code block. The same tag works in `.njk` and `.liquid` files.

`_redirect` is followed only when the form has allowed domains that include your site. See [Rules for _redirect](https://ikiform.com/docs/html.md#redirect-rules).

## Allowed domains

List production and each preview host. Eleventy sites are commonly deployed to Netlify or Cloudflare Pages, which handle previews differently.

| Where the form runs | Add to Allowed domains |
| --- | --- |
| `npx @11ty/eleventy --serve` | `localhost`. It matches `http://localhost:8080`: Ikiform compares the host name only, never the port. |
| Your domain | `example.com`, which also covers `www.example.com` |
| Netlify | `your-site.netlify.app`. Deploy previews (`deploy-preview-12--your-site.netlify.app`) and branch deploys (`branch--your-site.netlify.app`) are not subdomains of it, so add the exact preview host while you test one. |
| Cloudflare Pages | `your-project.pages.dev`. Preview URLs such as `abc123.your-project.pages.dev` are subdomains of it, so they already match. |

- Never add `netlify.app` or `pages.dev` on its own. Every site on that host would match, including other people's.

## Test checklist

1. Run the dev server and view the page source. The form should be plain HTML, not inside a `<pre><code>` block.
2. Submit locally with `localhost` in allowed domains and confirm the submission is listed in Ikiform.
3. Deploy a preview and submit again from the preview host.
4. Submit on production and confirm you land on your thank-you page.

## Troubleshooting

| What you see | Why | Fix |
| --- | --- | --- |
| The form shows as code on the page | The shortcode is indented four spaces in a Markdown file | Move the tag to the start of its line. |
| Half the form renders as paragraphs | The shortcode output contains a blank line, which ends Markdown's HTML block | Join the lines without blank ones, as in the config above. |
| `SyntaxError: Unexpected token 'export'` when Eleventy starts | The config uses ES module syntax in a CommonJS project | Rename it to `eleventy.config.mjs`, or add `"type": "module"` to `package.json`. |
| Nunjucks or Liquid can't find the `ikiform` tag | Eleventy didn't load the config file | Check the file is at the project root and named `eleventy.config.js` or `.mjs`. |
| Submissions arrive under the spam filter with `origin_mismatch` | The page that posted isn't on the form's allowed domains | Add the host from the table above. With **Reject** chosen, the same posts get `403 origin_not_allowed` instead. |

## Related

- [Hugo](https://ikiform.com/docs/guides/hugo.md): A partial and a shortcode for content files.
- [Jekyll](https://ikiform.com/docs/guides/jekyll.md): An include, and GitHub Pages setup.
- [HTML forms](https://ikiform.com/docs/html.md): Redirects, reserved fields and the honeypot.
- [Allowed domains](https://ikiform.com/docs/spam.md#allowed-domains): How origins are matched, and what a mismatch does.
