Skip to content

Forminit + Hugo Integration Guide

Hugo is a static site generator, which means it produces plain HTML files with no server-side processing. Forminit handles all form submissions through our client-side SDK, making it a perfect match for Hugo 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:

  • A Hugo 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 partial for your contact form:

<!-- layouts/partials/contact-form.html -->

<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 a partial for the form script or add it to your base layout:

<!-- layouts/partials/forminit-script.html -->

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

<script>
  const forminit = new Forminit();
  const FORM_ID = 'YOUR_FORM_ID'; // Replace with 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>

In your contact page layout or content:

<!-- layouts/_default/contact.html -->

{{ define "main" }}
<div class="contact-page">
  <h1>Contact Us</h1>
  
  {{ partial "contact-form.html" . }}
</div>
{{ end }}

{{ define "footer" }}
  {{ partial "forminit-script.html" . }}
{{ end }}

Or in your base template before </body>:

<!-- layouts/_default/baseof.html -->

<!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}">
<head>
  {{ partial "head.html" . }}
</head>
<body>
  {{ partial "header.html" . }}
  
  <main>
    {{ block "main" . }}{{ end }}
  </main>
  
  {{ partial "footer.html" . }}
  
  <!-- Add Forminit script on pages that need it -->
  {{ if .Params.hasForm }}
    {{ partial "forminit-script.html" . }}
  {{ end }}
</body>
</html>

Then in your contact page front matter:

---
title: "Contact"
hasForm: true
---

Store your Form ID in Hugo’s configuration for easier management:

# config.toml

[params]
  forminitFormId = "abc123xyz"

Or in hugo.yaml:

params:
  forminitFormId: "abc123xyz"

Then update your script partial:

<!-- layouts/partials/forminit-script.html -->

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

<script>
  const forminit = new Forminit();
  const FORM_ID = '{{ .Site.Params.forminitFormId }}';

  // ... rest of the script
</script>

For a complete list of available form field types and naming conventions, see the Form Blocks documentation.


<!-- layouts/partials/contact-form.html -->

<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>
<!-- layouts/partials/newsletter-form.html -->

<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.Params.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>
<!-- layouts/partials/feedback-form.html -->

<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>
<!-- layouts/partials/application-form.html -->

<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), you can use different Form IDs:

# config.toml

[params]
  [params.forms]
    contact = "frm_contact123"
    newsletter = "frm_news456"
    application = "frm_apply789"

Then in your partial:

<script>
  const FORM_ID = '{{ .Site.Params.forms.contact }}';
</script>

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 Hugo!"
    }
  },
  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 in Hugo:

---
# content/thank-you.md
title: "Thank You"
---

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

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