---
title: "Add forms to Middleman"
description: "Add a form backend to your Middleman (Ruby) static site. Handle submissions, file uploads, and notifications with no server required."
url: https://forminit.com/docs/middleman/
---

# Add forms to Middleman

Middleman is a Ruby-based 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 Middleman sites.

**What you'll get:**
- Accept form submissions without building a backend
- Email notifications for new submissions
- Spam protection (Cloudflare Turnstile, reCAPTCHA, hCaptcha, honeypot)
- File uploads support
- Submission dashboard to manage responses

**Requirements:**
- A Middleman site (v4.0+)
- Ruby 2.5+ and RubyGems
- A Forminit account and Form ID

---

## Quick Start

### Step 1: Create a Form on Forminit

1. Sign up or log in at [forminit.com](https://forminit.com)
2. Create a new form
3. Go to **Form Settings** → Set authentication mode to **Public**
4. Copy your **Form ID** (e.g., `YOUR-FORM-ID`)

> **Caution**
>
> Make sure authentication mode is set to Public in Form Settings.

---

### Step 2: Add Form ID to Configuration

Store your Form ID in `config.rb`:

```ruby
# config.rb

set :forminit_form_id, 'YOUR-FORM-ID'
```

Or use environment variables:

```ruby
# config.rb

set :forminit_form_id, ENV['FORMINIT_FORM_ID'] || 'YOUR-FORM-ID'
```

Create a `.env` file (use `dotenv` gem):

```
FORMINIT_FORM_ID=Xk9mP2nQ1r
```

---

### Step 3: Create a Form Partial

Create a partial for your contact form:

#### ERB (default)

```erb
<!-- source/partials/_contact_form.html.erb -->

<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>
```

#### Haml

```haml
-# source/partials/_contact_form.html.haml

%form#contact-form
  .form-group
    %label{for: "firstName"} First Name
    %input{type: "text", id: "firstName", name: "fi-sender-firstName", placeholder: "John", required: true}
  
  .form-group
    %label{for: "lastName"} Last Name
    %input{type: "text", id: "lastName", name: "fi-sender-lastName", placeholder: "Doe", required: true}
  
  .form-group
    %label{for: "email"} Email
    %input{type: "email", id: "email", name: "fi-sender-email", placeholder: "john@example.com", required: true}
  
  .form-group
    %label{for: "message"} Message
    %textarea{id: "message", name: "fi-text-message", placeholder: "Your message...", rows: 5, required: true}
  
  %button{type: "submit"} Send Message

%p#form-status
```

#### Slim

```html
/ source/partials/_contact_form.html.slim

form#contact-form
  .form-group
    label for="firstName" First Name
    input type="text" id="firstName" name="fi-sender-firstName" placeholder="John" required=true
  
  .form-group
    label for="lastName" Last Name
    input type="text" id="lastName" name="fi-sender-lastName" placeholder="Doe" required=true
  
  .form-group
    label for="email" Email
    input type="email" id="email" name="fi-sender-email" placeholder="john@example.com" required=true
  
  .form-group
    label for="message" Message
    textarea id="message" name="fi-text-message" placeholder="Your message..." rows=5 required=true
  
  button type="submit" Send Message

p#form-status
```

---

### Step 4: Add the Forminit SDK

Create a partial for the form script:

```erb
<!-- source/partials/_forminit_script.html.erb -->

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

<script>
  const forminit = new Forminit();
  const FORM_ID = '<%= config[:forminit_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>
```

---

### Step 5: Include in Your Layout

Add the script partial to your layout before `</body>`:

```erb
<!-- source/layouts/layout.html.erb -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= current_page.data.title || 'My Site' %></title>
  <%= stylesheet_link_tag 'site' %>
</head>
<body>
  <%= partial 'partials/header' %>
  
  <main>
    <%= yield %>
  </main>
  
  <%= partial 'partials/footer' %>
  
  <% if current_page.data.has_form %>
    <%= partial 'partials/forminit_script' %>
  <% end %>
</body>
</html>
```

---

### Step 6: Create Your Contact Page

Create a contact page that uses the form:

```erb
---
title: Contact
has_form: 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>
  
  <%= partial 'partials/contact_form' %>
</div>
```

---

## Using Data Files for Form Configuration

Store form configuration in Middleman's data folder:

```yaml
# data/forms.yml

contact:
  id: "Xk9mP2nQ1r"
  success_message: "Thanks for reaching out!"

newsletter:
  id: "aB3cD5eF7g"
  success_message: "You're subscribed!"

application:
  id: "T4vWx8yZ0q"
  success_message: "Application received!"
```

Access in your templates:

```erb
const FORM_ID = '<%= data.forms.contact.id %>';
```

---

## Form Field Reference

For a complete list of available form blocks (text, email, phone, file, rating, select, etc.) and field naming patterns, see the [Form Blocks documentation](https://forminit.com/docs/form-blocks/).

---

## Complete Examples

### Contact Form with Subject Selection

```erb
<!-- source/partials/_contact_form.html.erb -->

<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>
```

### Newsletter Signup

```erb
<!-- source/partials/_newsletter_form.html.erb -->

<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 = '<%= data.forms.newsletter.id %>';

  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>
```

### Feedback Form with Rating

```erb
<!-- source/partials/_feedback_form.html.erb -->

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

<p id="feedback-status"></p>
```

### Job Application with File Upload

```erb
<!-- source/partials/_application_form.html.erb -->

<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>
```

---

## Using Helpers for Form Elements

Create custom helpers in `config.rb` for cleaner templates:

```ruby
# config.rb

helpers do
  def forminit_form_id(form_name = :contact)
    data.forms[form_name]&.id || config[:forminit_form_id]
  end
  
  def forminit_script(form_element_id, form_name = :contact)
    <<~HTML
      <script src="https://forminit.com/sdk/v1/forminit.js"></script>
      <script>
        const forminit = new Forminit();
        const FORM_ID = '#{forminit_form_id(form_name)}';
        const form = document.getElementById('#{form_element_id}');
        const status = document.getElementById('#{form_element_id}-status');
        
        form.addEventListener('submit', async function(event) {
          event.preventDefault();
          status.textContent = 'Sending...';
          
          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;
          }
          
          status.textContent = 'Message sent successfully!';
          status.className = 'status-success';
          form.reset();
        });
      </script>
    HTML
  end
end
```

Use in templates:

```erb
<form id="contact-form">
  <!-- form fields -->
</form>
<p id="contact-form-status"></p>

<%= forminit_script('contact-form', :contact) %>
```

---

## Handling Form Submission Response

The SDK returns `{ data, redirectUrl, error }`:

**On success:**

```javascript
{
  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 Middleman!"
    }
  },
  redirectUrl: "https://forminit.com/thank-you"
}
```

**On error:**

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

---

## Redirect After Submission

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

```javascript
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:

```erb
---
title: Thank You
---

<h1>Thank You!</h1>
<p>Thank you for your message! We'll get back to you soon.</p>
<p><a href="/">← Back to Home</a></p>
```

---

## Project Structure Example

Here's a typical Middleman project structure with Forminit forms:

```
my-middleman-site/
├── config.rb                      # Site config with Form ID
├── data/
│   └── forms.yml                  # Form configurations
├── source/
│   ├── images/
│   ├── javascripts/
│   ├── layouts/
│   │   └── layout.html.erb        # Base layout
│   ├── partials/
│   │   ├── _contact_form.html.erb
│   │   ├── _forminit_script.html.erb
│   │   ├── _header.html.erb
│   │   └── _footer.html.erb
│   ├── stylesheets/
│   │   └── site.css.scss
│   ├── contact.html.erb           # Contact page
│   ├── thank-you.html.erb         # Thank you page
│   └── index.html.erb             # Homepage
├── Gemfile
└── Gemfile.lock
```

---

## Build and Deploy

Build your static site:

```bash
bundle exec middleman build
```

The generated files will be in the `build/` directory, ready to deploy to any static hosting service like Netlify, Vercel, or AWS S3.

---

## Next Steps

1. [View your submissions](https://app.forminit.com) in the Forminit dashboard
2. Set up [email notifications](https://forminit.com/docs/email-notifications/) for new submissions
3. Explore [webhook integrations](https://forminit.com/docs/webhooks/) for advanced workflows
