integrations · static site generator

A contact form for Hugo, no server runtime required.

Hugo has no server runtime: it compiles your whole site to static HTML at build time, so there's nothing running to receive a form submission. Point a plain <form> at your mailcontact endpoint instead and submissions land in your project's inbox, with a notification in Discord, Slack, email, or a webhook. No serverless function, no mail service, no Hugo module.

Because Hugo templates output plain HTML, the form lives in a single partial (layouts/partials/contact-form.html) that you pull into any page, layout, or shortcode with {{ partial "contact-form.html" . }}. Write it once, reuse it everywhere.

The built site is just files, so it deploys (and the form keeps working) on any static host: GitHub Pages, Cloudflare Pages, Netlify, or an S3 bucket behind a CDN.

recommended setup

Use the MailContact script with your own design.

Add this HTML to your site, or use the form builder. Replace the form link with yours. The hosted script injects the hidden check, handles sending and automatic verification, and preserves the draft on errors. You control the HTML and CSS. Free forms display a visible “Processed by mailcontact.app” link; Pro and Plus do not. Feedback follows the browser’s language; set data-mailcontact-language="fr" on the form to force French.

contact.html
<form action="https://mailcontact.app/api/forms/your-form-id" method="POST" data-mailcontact-form>
  <fieldset>
    <legend>Contact us</legend>
    <label>
      Name (optional)
      <input name="name" type="text" autocomplete="name" maxlength="200" />
    </label>
    <label>
      Email
      <input name="email" type="email" autocomplete="email" maxlength="320" required />
    </label>
    <label>
      Message
      <textarea name="message" rows="5" maxlength="5000" required></textarea>
    </label>
    <noscript>
    <div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden;">
      <label>Website
        <input name="company_website" type="text" tabindex="-1" autocomplete="off" />
      </label>
    </div>
    </noscript>
    <button type="submit">Send message</button>
  </fieldset>
  <p data-contact-status role="status" aria-live="polite" aria-atomic="true"></p>
</form>
<script defer src="https://mailcontact.app/forms/v1.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

For a component with its own submit handler, load the same script and use MailContact.prepare(form) and MailContact.send() instead of the automatic marker. See the custom interface reference for loading, feedback and Free attribution.

the snippet

Alternative: a direct integration for Hugo.

This example posts directly to the API. Keep the required company_website field present and empty. Native posts open JSON on success or a verification page when needed. Custom scripts without verification support send suspicious messages to Spam for review; strong spam is also held there without notifications or quota usage. A success response confirms reception, including review, or a silent discard of a filled check. See spam protection for the full behavior.

layouts/partials/contact-form.html
{{/* layouts/partials/contact-form.html */}}
<form action="https://mailcontact.app/api/forms/your-form-id" method="POST">
  <label for="name">Name</label>
  <input type="text" id="name" name="name" />

  <label for="email">Email</label>
  <input type="email" id="email" name="email" required />

  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <button type="submit">Send</button>
  <input name="company_website" type="text" tabindex="-1" autocomplete="off"
    style="position:absolute;left:-10000px" aria-hidden="true" />
</form>

how it works

Three steps, start to finish.

01

Create an inbox

One inbox per project. Name it after your site and you get a form endpoint and a project email address immediately.

02

Add the partial

Save the snippet as layouts/partials/contact-form.html, then {{ partial "contact-form.html" . }} on your contact page or in a layout. It's your markup; style it with your theme's CSS.

03

Get notified

Add a Discord, Slack, email, or webhook channel. Delivered messages ping you where you already are.

why mailcontact

Built to pair well with Hugo.

No server runtime to add

Hugo emits static files and stops there: there's no runtime to receive a POST. Posting to an external endpoint means you never bolt a serverless function onto your build just to run a form.

One partial, every template

Define contact-form.html once and pull it into any single page, list template, or shortcode with {{ partial }}. Change the form in one place and every page updates.

Deploys wherever Hugo does

The form is part of your static output, so it travels with the site to GitHub Pages, Cloudflare Pages, Netlify, or an S3 bucket. No host-specific form feature required.

faq

Hugo + mailcontact, answered

Does a Hugo contact form need a backend or serverless function?

No. Hugo only builds static HTML, so the form posts directly to your mailcontact endpoint, which stores the message and sends your notifications. Nothing runs on your side.

Does it work on GitHub Pages, Cloudflare Pages, or S3?

Yes. The endpoint accepts posts from any origin, so the form works wherever your Hugo output is served, including hosts with no form handling of their own, like GitHub Pages or a plain S3 bucket.

Can I style the form to match my Hugo theme?

Completely. mailcontact only receives the POST. The markup in the partial is yours, so your theme's stylesheet applies. Add classes, rearrange fields, or wrap it in your own layout.

How do I show a success message after someone submits?

Use the hosted MailContact script or MailContact.send() for submission, verification and inline feedback (the endpoint returns JSON like {"ok": true}). A configurable redirect for native form posts is not currently available.

related

Also works with

One inbox for every Hugo project you ship.

Forms and a real email address, together, with notifications where you live. Start with a free account.

Create your free form

No credit card needed. Free includes one project and 50 form messages per month.