Contact Form for Hugo
Build a contact form partial for your Hugo site that sends submissions directly to your email without any backend server.
Overview
This guide shows how to create a reusable contact form partial in Hugo that works without a backend. The form sends submissions directly to CatchForm, which delivers them to your inbox.
Prerequisites
- A Hugo site (v0.100 or later)
- A CatchForm account with an active form
Step 1: Set Up CatchForm
Sign up for CatchForm and create a new form. You'll receive a unique token. Store it in your Hugo site's config or pass it via parameters.
Option A: Add it to your config file (hugo.toml):
[params]
catchformToken = "your-token-here"
Step 2: Create the Contact Form Partial
Create a new file layouts/partials/contact-form.html:
<form
action="https://catchform.dev/f/{{ .Site.Params.catchformToken }}"
method="POST"
class="contact-form"
>
<input
type="hidden"
name="_gotcha"
value=""
>
<div class="form-group">
<label for="name">Name</label>
<input
id="name"
type="text"
name="name"
required
>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
id="email"
type="email"
name="email"
required
>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea
id="message"
name="message"
rows="5"
required
></textarea>
</div>
<button type="submit">Send Message</button>
</form>
<style>
.contact-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 500px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.contact-form label {
font-weight: 500;
font-size: 0.95rem;
}
.contact-form input,
.contact-form textarea {
padding: 0.625rem;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
font-size: 1rem;
}
.contact-form input:focus,
.contact-form textarea:focus {
outline: none;
border-color: #4f46e5;
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1);
}
.contact-form button {
padding: 0.75rem 1.5rem;
background-color: #4f46e5;
color: white;
border: none;
border-radius: 4px;
font-weight: 600;
cursor: pointer;
font-size: 1rem;
}
.contact-form button:hover {
background-color: #4338ca;
}
.contact-form button:active {
transform: scale(0.98);
}
</style>
Step 3: Use the Partial
Include the partial in any layout or content page using the partial function:
<!-- layouts/contact.html or content/contact/_index.md -->
{{ define "main" }}
<main>
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ partial "contact-form" . }}
</main>
{{ end }}
In Markdown files, use shortcodes to include the partial:
{{< partial "contact-form" . >}}
Customization
Add a Success Message
After form submission, redirect the user to a success page. In your CatchForm dashboard, configure the redirect URL (e.g., /thank-you), then create a corresponding Hugo page.
Add More Fields
Simply add more form groups to the partial. Change the name attribute to customize how the field appears in your email:
<div class="form-group">
<label for="phone">Phone</label>
<input
id="phone"
type="tel"
name="phone"
>
</div>
Pass Token as Parameter
If you prefer not to store the token in config, pass it as a parameter to the partial:
<!-- In your layout file -->
{{ partial "contact-form" (dict "token" "your-token-here") }}
<!-- In the partial -->
<form action="https://catchform.dev/f/{{ .token }}" method="POST">
...
</form>
Style with Your Theme
The partial uses inline scoped styles. Replace them with your theme's CSS or remove the <style> block and style via your external stylesheet.
Pro Tips
File Attachments
Pro plan users can accept file uploads. Add an input with type="file" and add enctype="multipart/form-data" to the form. Allowed types: JPEG, PNG, GIF, PDF. Maximum size: 5 MB.
Validation
The form uses HTML5 validation attributes (required, type="email"). Browsers enforce these, but CatchForm also validates on the server side.
Spam Protection
The honeypot field is already included in the partial. Bots will fill it and their submissions will be rejected automatically.
Next Steps
Your form is now live. Check your inbox for submissions. Need more help?