Skip to content

Forminit + Eleventy (11ty) Integration Guide

Eleventy is a simple, flexible static site generator that outputs plain HTML files with no server-side processing. Forminit handles all form submissions through our client-side SDK, making it a perfect match for Eleventy sites.

What you’ll get:

  • Accept form submissions without building a backend
  • Email notifications for new submissions
  • Spam protection (reCAPTCHA, hCaptcha, honeypot)
  • File uploads support
  • Submission dashboard to manage responses

Requirements:

  • An Eleventy site (any version)
  • A Forminit account and Form ID

  1. Sign up or log in at forminit.com
  2. Create a new form
  3. Go to Form Settings → Set authentication mode to Public
  4. Copy your Form ID (e.g., abc123xyz)

Create a new include file for your contact form. Eleventy supports multiple templating languages—choose your preferred one:

<!-- _includes/contact-form.njk -->

<form id="contact-form">
  <div class="form-group">
    <label for="firstName">First Name</label>
    <input 
      type="text" 
      id="firstName" 
      name="fi-sender-firstName" 
      placeholder="John" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="lastName">Last Name</label>
    <input 
      type="text" 
      id="lastName" 
      name="fi-sender-lastName" 
      placeholder="Doe" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="email">Email</label>
    <input 
      type="email" 
      id="email" 
      name="fi-sender-email" 
      placeholder="john@example.com" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="message">Message</label>
    <textarea 
      id="message" 
      name="fi-text-message" 
      placeholder="Your message..." 
      rows="5" 
      required
    ></textarea>
  </div>

  <button type="submit">Send Message</button>
</form>

<p id="form-status"></p>
<!-- _includes/contact-form.liquid -->

<form id="contact-form">
  <div class="form-group">
    <label for="firstName">First Name</label>
    <input 
      type="text" 
      id="firstName" 
      name="fi-sender-firstName" 
      placeholder="John" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="lastName">Last Name</label>
    <input 
      type="text" 
      id="lastName" 
      name="fi-sender-lastName" 
      placeholder="Doe" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="email">Email</label>
    <input 
      type="email" 
      id="email" 
      name="fi-sender-email" 
      placeholder="john@example.com" 
      required 
    />
  </div>

  <div class="form-group">
    <label for="message">Message</label>
    <textarea 
      id="message" 
      name="fi-text-message" 
      placeholder="Your message..." 
      rows="5" 
      required
    ></textarea>
  </div>

  <button type="submit">Send Message</button>
</form>

<p id="form-status"></p>

Create an include for the form script:

<!-- _includes/forminit-script.njk -->

<script src="https://forminit.com/sdk/v1/forminit.js"></script>

<script>
  const forminit = new Forminit();
  const FORM_ID = '{{ site.forminitFormId or "YOUR_FORM_ID" }}';

  const form = document.getElementById('contact-form');
  const status = document.getElementById('form-status');

  form.addEventListener('submit', async function(event) {
    event.preventDefault();
    
    // Show loading state
    status.textContent = 'Sending...';
    status.className = 'status-loading';

    const formData = new FormData(form);
    const { data, error } = await forminit.submit(FORM_ID, formData);

    if (error) {
      status.textContent = error.message;
      status.className = 'status-error';
      return;
    }

    // Success
    status.textContent = 'Message sent successfully!';
    status.className = 'status-success';
    form.reset();
  });
</script>
<!-- _includes/forminit-script.liquid -->

<script src="https://forminit.com/sdk/v1/forminit.js"></script>

<script>
  const forminit = new Forminit();
  const FORM_ID = '{{ site.forminitFormId | default: "YOUR_FORM_ID" }}';

  const form = document.getElementById('contact-form');
  const status = document.getElementById('form-status');

  form.addEventListener('submit', async function(event) {
    event.preventDefault();
    
    // Show loading state
    status.textContent = 'Sending...';
    status.className = 'status-loading';

    const formData = new FormData(form);
    const { data, error } = await forminit.submit(FORM_ID, formData);

    if (error) {
      status.textContent = error.message;
      status.className = 'status-error';
      return;
    }

    // Success
    status.textContent = 'Message sent successfully!';
    status.className = 'status-success';
    form.reset();
  });
</script>

Add the form and script to your page layout:

<!-- _includes/layouts/base.njk -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }} | {{ site.name }}</title>
</head>
<body>
  {% include "header.njk" %}
  
  <main>
    {{ content | safe }}
  </main>
  
  {% include "footer.njk" %}
  
  <!-- Add Forminit script on pages that need it -->
  {% if hasForm %}
    {% include "forminit-script.njk" %}
  {% endif %}
</body>
</html>
<!-- _includes/layouts/base.liquid -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }} | {{ site.name }}</title>
</head>
<body>
  {% include "header.liquid" %}
  
  <main>
    {{ content }}
  </main>
  
  {% include "footer.liquid" %}
  
  <!-- Add Forminit script on pages that need it -->
  {% if hasForm %}
    {% include "forminit-script.liquid" %}
  {% endif %}
</body>
</html>

Create a contact page that uses the form:

---
layout: layouts/base.njk
title: Contact
hasForm: true
---

<div class="contact-page">
  <h1>Contact Us</h1>
  <p>We'd love to hear from you. Fill out the form below and we'll get back to you soon.</p>
  
  {% include "contact-form.njk" %}
</div>
---
layout: layouts/base.njk
title: Contact
hasForm: true
---

# Contact Us

We'd love to hear from you. Fill out the form below and we'll get back to you soon.

{% include "contact-form.njk" %}

Store your Form ID in Eleventy’s global data for easier management:

Create a global data file:

// _data/site.json

{
  "name": "My Site",
  "forminitFormId": "abc123xyz"
}

Then reference it in your script:

<!-- Nunjucks -->
const FORM_ID = '{{ site.forminitFormId }}';

<!-- Liquid -->
const FORM_ID = '{{ site.forminitFormId }}';

For more security, use environment variables with a JavaScript data file:

// _data/site.js

module.exports = {
  name: "My Site",
  forminitFormId: process.env.FORMINIT_FORM_ID || "abc123xyz"
};

Then set the environment variable when building:

FORMINIT_FORM_ID=frm_abc123xyz npx @11ty/eleventy

For a complete list of available form blocks (text, email, phone, file, rating, select, etc.) and field naming patterns, see the Form Blocks documentation.


<!-- _includes/contact-form.njk -->

<form id="contact-form">
  <input type="text" name="fi-sender-firstName" placeholder="First name" required />
  <input type="text" name="fi-sender-lastName" placeholder="Last name" required />
  <input type="email" name="fi-sender-email" placeholder="Email" required />
  
  <select name="fi-select-subject" required>
    <option value="">Select a subject</option>
    <option value="general">General Inquiry</option>
    <option value="support">Support</option>
    <option value="sales">Sales</option>
    <option value="partnership">Partnership</option>
  </select>
  
  <textarea name="fi-text-message" placeholder="Your message" required></textarea>
  
  <button type="submit">Send</button>
</form>

<p id="form-status"></p>
<!-- _includes/newsletter-form.njk -->

<form id="newsletter-form">
  <input type="email" name="fi-sender-email" placeholder="Enter your email" required />
  <button type="submit">Subscribe</button>
</form>

<p id="newsletter-status"></p>

<script src="https://forminit.com/sdk/v1/forminit.js"></script>
<script>
  const forminit = new Forminit();
  const FORM_ID = '{{ site.newsletterFormId }}';

  const form = document.getElementById('newsletter-form');
  const status = document.getElementById('newsletter-status');

  form.addEventListener('submit', async function(event) {
    event.preventDefault();
    status.textContent = 'Subscribing...';

    const formData = new FormData(form);
    const { data, error } = await forminit.submit(FORM_ID, formData);

    if (error) {
      status.textContent = error.message;
      return;
    }

    status.textContent = 'Thank you for subscribing!';
    form.reset();
  });
</script>
<!-- _includes/feedback-form.njk -->

<form id="feedback-form">
  <input type="email" name="fi-sender-email" placeholder="Email (optional)" />
  
  <fieldset>
    <legend>How would you rate your experience?</legend>
    <label><input type="radio" name="fi-rating-experience" value="1" /> 1</label>
    <label><input type="radio" name="fi-rating-experience" value="2" /> 2</label>
    <label><input type="radio" name="fi-rating-experience" value="3" /> 3</label>
    <label><input type="radio" name="fi-rating-experience" value="4" /> 4</label>
    <label><input type="radio" name="fi-rating-experience" value="5" /> 5</label>
  </fieldset>
  
  <textarea name="fi-text-feedback" placeholder="Tell us more (optional)"></textarea>
  
  <button type="submit">Submit Feedback</button>
</form>

<p id="feedback-status"></p>
<!-- _includes/application-form.njk -->

<form id="application-form">
  <input type="text" name="fi-sender-firstName" placeholder="First name" required />
  <input type="text" name="fi-sender-lastName" placeholder="Last name" required />
  <input type="email" name="fi-sender-email" placeholder="Email" required />
  <input type="tel" name="fi-sender-phone" placeholder="Phone (+1234567890)" />
  
  <input type="url" name="fi-url-linkedin" placeholder="LinkedIn profile URL" />
  <input type="url" name="fi-url-portfolio" placeholder="Portfolio URL" />
  
  <select name="fi-select-position" required>
    <option value="">Select position</option>
    <option value="frontend">Frontend Developer</option>
    <option value="backend">Backend Developer</option>
    <option value="fullstack">Full Stack Developer</option>
    <option value="designer">UI/UX Designer</option>
  </select>
  
  <label>
    Resume (PDF)
    <input type="file" name="fi-file-resume" accept=".pdf" required />
  </label>
  
  <label>
    Cover Letter (optional)
    <input type="file" name="fi-file-cover-letter" accept=".pdf,.doc,.docx" />
  </label>
  
  <textarea name="fi-text-why-join" placeholder="Why do you want to join us?"></textarea>
  
  <button type="submit">Submit Application</button>
</form>

<p id="application-status"></p>

If you have multiple forms (contact, newsletter, application), store different Form IDs in your data file:

// _data/site.json

{
  "name": "My Site",
  "forms": {
    "contact": "abc123",
    "newsletter": "xyz456",
    "application": "sms55"
  }
}

Then reference them in your templates:

<!-- Nunjucks -->
const FORM_ID = '{{ site.forms.contact }}';

<!-- Liquid -->
const FORM_ID = '{{ site.forms.contact }}';

Create a shortcode for reusable forms across your site:

// eleventy.config.js (or .eleventy.js)

module.exports = function(eleventyConfig) {
  
  // Contact form shortcode
  eleventyConfig.addShortcode("contactForm", function(formId) {
    return `
      <form id="contact-form">
        <input type="text" name="fi-sender-firstName" placeholder="First name" required />
        <input type="text" name="fi-sender-lastName" placeholder="Last name" required />
        <input type="email" name="fi-sender-email" placeholder="Email" required />
        <textarea name="fi-text-message" placeholder="Message" required></textarea>
        <button type="submit">Send</button>
      </form>
      <p id="form-status"></p>
      
      <script src="https://forminit.com/sdk/v1/forminit.js"></script>
      <script>
        (function() {
          const forminit = new Forminit();
          const form = document.getElementById('contact-form');
          const status = document.getElementById('form-status');
          
          form.addEventListener('submit', async function(e) {
            e.preventDefault();
            status.textContent = 'Sending...';
            
            const { data, error } = await forminit.submit('${formId}', new FormData(form));
            
            if (error) {
              status.textContent = error.message;
              return;
            }
            
            status.textContent = 'Message sent!';
            form.reset();
          });
        })();
      </script>
    `;
  });

  return {
    dir: {
      input: "src",
      output: "_site"
    }
  };
};

Use the shortcode in your pages:

---
title: Contact
---

# Contact Us

{% contactForm "abc123xyz" %}

The SDK returns { data, redirectUrl, error }:

On success:

{
  data: {
    hashId: "7LMIBoYY74JOCp1k",      // Unique submission ID
    date: "2026-01-01 21:10:24",      // Timestamp
    blocks: {                          // Submitted values
      sender: {
        firstName: "John",
        lastName: "Doe",
        email: "john@example.com"
      },
      message: "Hello from Eleventy!"
    }
  },
  redirectUrl: "https://forminit.com/thank-you"
}

On error:

{
  error: {
    error: "ERROR_CODE",
    code: 400,
    message: "Human-readable error message"
  }
}

If you prefer to redirect users to a thank-you page:

form.addEventListener('submit', async function(event) {
  event.preventDefault();

  const formData = new FormData(form);
  const { data, redirectUrl, error } = await forminit.submit(FORM_ID, formData);

  if (error) {
    status.textContent = error.message;
    return;
  }

  // Redirect to thank-you page
  window.location.href = '/thank-you/';
  // Or use Forminit's redirect URL:
  // window.location.href = redirectUrl;
});

Create a thank-you page:

---
# src/thank-you.md
layout: layouts/base.njk
title: Thank You
---

# Thank You!

Thank you for your message! We'll get back to you soon.

[← Back to Home](/)

Here’s a typical Eleventy project structure with Forminit forms:

my-eleventy-site/
├── _data/
│   └── site.json              # Global data including Form IDs
├── _includes/
│   ├── layouts/
│   │   └── base.njk           # Base layout
│   ├── contact-form.njk       # Form HTML
│   ├── forminit-script.njk    # Form submission script
│   ├── header.njk
│   └── footer.njk
├── src/
│   ├── css/
│   │   └── forms.css          # Form styles
│   ├── contact.njk            # Contact page
│   ├── thank-you.md           # Thank you page
│   └── index.njk              # Homepage
├── eleventy.config.js         # Eleventy configuration
└── package.json

  1. View your submissions in the Forminit dashboard
  2. Set up email notifications for new submissions
  3. Explore webhook integrations for advanced workflows