---
title: "Add forms to Neocities"
description: "Add working contact forms to your Neocities website. Handle submissions, file uploads, and notifications, with no server-side code required."
url: https://forminit.com/docs/neocities/
---

# Add forms to Neocities

<a href="https://neocities.org" target="_blank" rel="nofollow noopener noreferrer">Neocities</a> is a free static website hosting service that lets you create HTML, CSS, and JavaScript websites. Since Neocities doesn't support server-side code (like PHP or Node.js), you need a form backend service to handle form submissions.

**Forminit** handles everything for you:

- **Receives and stores submissions** — No database setup required
- **Email notifications** — Get notified when someone submits your form
- **Spam protection** — Built-in rate limiting and optional CAPTCHA support
- **File uploads** — Accept files up to 25 MB
- **Validation** — Automatic email, phone, and URL validation

**Requirements:**

- A <a href="https://neocities.org" target="_blank" rel="nofollow noopener noreferrer">Neocities</a> account with a website
- A Forminit account and Form ID

---

## Quick Start

### Step 1: Create a Form in Forminit

1. Sign up or log in at [forminit.com](https://forminit.com)
2. Create a new form from your dashboard
3. Copy your **Form ID** (looks like `frm_abc123xyz`)
4. **Important:** Set authentication mode to **"Public"** in Form Settings

### Step 2: Add the Form HTML

Create or edit your HTML file on Neocities:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact Me</title>
  <style>
    * {
      box-sizing: border-box;
    }
    
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      max-width: 500px;
      margin: 40px auto;
      padding: 20px;
      background: #f5f5f5;
    }
    
    h1 {
      margin-bottom: 24px;
    }
    
    form {
      background: white;
      padding: 24px;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    
    label {
      display: block;
      margin-bottom: 4px;
      font-weight: 500;
    }
    
    input, textarea {
      width: 100%;
      padding: 10px;
      margin-bottom: 16px;
      border: 1px solid #ddd;
      border-radius: 4px;
      font-size: 16px;
    }
    
    textarea {
      min-height: 120px;
      resize: vertical;
    }
    
    button {
      background: #4f46e5;
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: 4px;
      font-size: 16px;
      cursor: pointer;
      width: 100%;
    }
    
    button:hover {
      background: #4338ca;
    }
    
    button:disabled {
      background: #9ca3af;
      cursor: not-allowed;
    }
    
    #form-status {
      margin-top: 16px;
      padding: 12px;
      border-radius: 4px;
      display: none;
    }
    
    #form-status.success {
      display: block;
      background: #d1fae5;
      color: #065f46;
    }
    
    #form-status.error {
      display: block;
      background: #fee2e2;
      color: #991b1b;
    }
  </style>
</head>
<body>
  <h1>Contact Me</h1>
  
  <form id="contact-form">
    <label for="firstName">First Name</label>
    <input type="text" id="firstName" name="fi-sender-firstName" required>
    
    <label for="lastName">Last Name</label>
    <input type="text" id="lastName" name="fi-sender-lastName" required>
    
    <label for="email">Email</label>
    <input type="email" id="email" name="fi-sender-email" required>
    
    <label for="message">Message</label>
    <textarea id="message" name="fi-text-message" required></textarea>
    
    <button type="submit" id="submit-btn">Send Message</button>
  </form>
  
  <div id="form-status"></div>

  <!-- Forminit SDK -->
  <script src="https://forminit.com/sdk/v1/forminit.js"></script>
  
  <script>
    // Initialize Forminit
    const forminit = new Forminit();
    
    // Replace with your Form ID from Forminit dashboard
    const FORM_ID = 'YOUR_FORM_ID';
    
    const form = document.getElementById('contact-form');
    const submitBtn = document.getElementById('submit-btn');
    const statusDiv = document.getElementById('form-status');
    
    form.addEventListener('submit', async function(event) {
      event.preventDefault();
      
      // Disable button while submitting
      submitBtn.disabled = true;
      submitBtn.textContent = 'Sending...';
      statusDiv.className = '';
      statusDiv.style.display = 'none';
      
      // Create FormData from the form
      const formData = new FormData(form);
      
      // Submit to Forminit
      const { data, error } = await forminit.submit(FORM_ID, formData);
      
      if (error) {
        // Show error message
        statusDiv.textContent = error.message || 'Something went wrong. Please try again.';
        statusDiv.className = 'error';
        submitBtn.disabled = false;
        submitBtn.textContent = 'Send Message';
        return;
      }
      
      // Success!
      statusDiv.textContent = 'Thank you! Your message has been sent.';
      statusDiv.className = 'success';
      form.reset();
      submitBtn.disabled = false;
      submitBtn.textContent = 'Send Message';
    });
  </script>
</body>
</html>
```

### Step 3: Replace the Form ID

Find this line in the code:

```javascript
const FORM_ID = 'YOUR_FORM_ID';
```

Replace `YOUR_FORM_ID` with your actual Form ID from Forminit (e.g., `frm_abc123xyz`).

### Step 4: Upload to Neocities

1. Log in to your Neocities dashboard
2. Upload the HTML file (or edit an existing page)
3. Visit your page and test the form

---

## Field Naming Convention

Forminit uses a special naming convention for form fields. All field names must start with `fi-` followed by the block type and field name.

### Sender Fields (Submitter Information)

Use these for collecting information about the person filling out the form:

| Field | Name Attribute | Description |
|-------|----------------|-------------|
| Email | `fi-sender-email` | Submitter's email address |
| First Name | `fi-sender-firstName` | First name |
| Last Name | `fi-sender-lastName` | Last name |
| Full Name | `fi-sender-fullName` | Full name (use instead of first/last) |
| Phone | `fi-sender-phone` | Phone number (E.164 format) |
| Company | `fi-sender-company` | Company name |
| Position | `fi-sender-position` | Job title |

**At least one** sender field is required (email, name, or phone).

### Custom Fields

For additional data, use the appropriate block type:

| Type | Pattern | Example | Use For |
|------|---------|---------|---------|
| Text | `fi-text-{name}` | `fi-text-message` | Messages, comments |
| Number | `fi-number-{name}` | `fi-number-quantity` | Quantities, amounts |
| Email | `fi-email-{name}` | `fi-email-referral` | Additional emails |
| Phone | `fi-phone-{name}` | `fi-phone-emergency` | Additional phones |
| URL | `fi-url-{name}` | `fi-url-website` | Links |
| Date | `fi-date-{name}` | `fi-date-appointment` | Dates (ISO 8601) |
| Rating | `fi-rating-{name}` | `fi-rating-satisfaction` | Ratings (1-5) |
| Select | `fi-select-{name}` | `fi-select-category` | Dropdowns, radios |
| Country | `fi-country-{name}` | `fi-country-shipping` | Country codes |
| File | `fi-file-{name}` | `fi-file-attachment` | File uploads |

For a complete reference, see the [Form Blocks documentation](https://forminit.com/docs/form-blocks/).

---

## Examples

### Feedback Form with Rating

```html
<form id="feedback-form">
  <label for="email">Your Email</label>
  <input type="email" name="fi-sender-email" id="email" required>
  
  <label for="rating">How would you rate your experience? (1-5)</label>
  <input type="number" name="fi-rating-experience" id="rating" min="1" max="5" required>
  
  <label for="feedback">Your Feedback</label>
  <textarea name="fi-text-feedback" id="feedback" required></textarea>
  
  <button type="submit">Submit Feedback</button>
</form>
```

### Newsletter Signup

```html
<form id="newsletter-form">
  <label for="email">Email Address</label>
  <input type="email" name="fi-sender-email" id="email" required>
  
  <label for="name">Name (optional)</label>
  <input type="text" name="fi-sender-fullName" id="name">
  
  <button type="submit">Subscribe</button>
</form>
```

### Contact Form with Subject Dropdown

```html
<form id="contact-form">
  <label for="name">Your Name</label>
  <input type="text" name="fi-sender-fullName" id="name" required>
  
  <label for="email">Email</label>
  <input type="email" name="fi-sender-email" id="email" required>
  
  <label for="subject">Subject</label>
  <select name="fi-select-subject" id="subject" required>
    <option value="">Select a subject...</option>
    <option value="General Inquiry">General Inquiry</option>
    <option value="Collaboration">Collaboration</option>
    <option value="Bug Report">Bug Report</option>
    <option value="Other">Other</option>
  </select>
  
  <label for="message">Message</label>
  <textarea name="fi-text-message" id="message" required></textarea>
  
  <button type="submit">Send</button>
</form>
```

### File Upload Form

```html
<form id="upload-form">
  <label for="name">Your Name</label>
  <input type="text" name="fi-sender-fullName" id="name" required>
  
  <label for="email">Email</label>
  <input type="email" name="fi-sender-email" id="email" required>
  
  <label for="file">Attach File (PDF, images, max 25MB)</label>
  <input type="file" name="fi-file-attachment" id="file" accept=".pdf,.jpg,.jpeg,.png,.gif">
  
  <label for="notes">Notes</label>
  <textarea name="fi-text-notes" id="notes"></textarea>
  
  <button type="submit">Upload</button>
</form>
```

> **Note**
>
> File uploads count toward the 25 MB per submission limit. See [File Uploads documentation](https://forminit.com/docs/file-upload/) for more details.

---

## Complete JavaScript Template

Here's a reusable JavaScript template you can adapt for any form:

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

<script>
(function() {
  const forminit = new Forminit();
  const FORM_ID = 'YOUR_FORM_ID'; // Replace with your Form ID
  
  const form = document.getElementById('contact-form');
  const submitBtn = form.querySelector('button[type="submit"]');
  const statusDiv = document.getElementById('form-status');
  
  // Store original button text
  const originalBtnText = submitBtn.textContent;
  
  form.addEventListener('submit', async function(event) {
    event.preventDefault();
    
    // UI: Show loading state
    submitBtn.disabled = true;
    submitBtn.textContent = 'Sending...';
    hideStatus();
    
    try {
      const formData = new FormData(form);
      const { data, error } = await forminit.submit(FORM_ID, formData);
      
      if (error) {
        showStatus(error.message || 'An error occurred. Please try again.', 'error');
        return;
      }
      
      // Success
      showStatus('Thank you! Your submission was received.', 'success');
      form.reset();
      
    } catch (err) {
      showStatus('Network error. Please check your connection and try again.', 'error');
    } finally {
      // Reset button
      submitBtn.disabled = false;
      submitBtn.textContent = originalBtnText;
    }
  });
  
  function showStatus(message, type) {
    statusDiv.textContent = message;
    statusDiv.className = type;
    statusDiv.style.display = 'block';
  }
  
  function hideStatus() {
    statusDiv.style.display = 'none';
    statusDiv.className = '';
  }
})();
</script>
```

---

## Response Handling

When a form is submitted successfully, Forminit returns:

```javascript
{
  data: {
    hashId: "7LMIBoYY74JOCp1k",      // Unique submission ID
    date: "2026-01-17 14:30:00",     // Submission timestamp
    blocks: {                         // Submitted values
      sender: {
        firstName: "John",
        lastName: "Doe",
        email: "john@example.com"
      },
      message: "Hello!"
    }
  },
  redirectUrl: "https://forminit.com/thank-you"
}
```

If there's an error:

```javascript
{
  error: {
    error: "FI_SCHEMA_FORMAT_EMAIL",
    code: 400,
    message: "Invalid email format. Please enter a valid email address."
  }
}
```

---

## Redirecting After Submission

If you want to redirect users to a thank-you page instead of showing an inline message:

```javascript
const { data, redirectUrl, error } = await forminit.submit(FORM_ID, formData);

if (error) {
  // Handle error
  return;
}

// Redirect to thank you page
window.location.href = '/thanks.html';

// Or use Forminit's default thank you page:
// window.location.href = redirectUrl;
```

---

## Validation

Forminit automatically validates:

| Field Type | Validation |
|------------|------------|
| Email | Valid email format |
| Phone | E.164 format (+12025550123) |
| URL | Valid URL format |
| Rating | Must be 1-5 |
| Country | ISO 3166-1 alpha-2 code |
| Date | ISO 8601 format |

### Client-Side Validation Tips

Add HTML5 validation attributes for better user experience:

```html
<!-- Required field -->
<input type="email" name="fi-sender-email" required>

<!-- Email pattern -->
<input type="email" name="fi-sender-email" 
       pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$">

<!-- Phone with pattern -->
<input type="tel" name="fi-sender-phone" 
       pattern="\+[0-9]{10,15}" 
       placeholder="+12025550123">

<!-- Rating range -->
<input type="number" name="fi-rating-score" min="1" max="5">

<!-- Text length -->
<textarea name="fi-text-message" minlength="10" maxlength="1000"></textarea>
```

---

## Spam Protection

### Option 1: Honeypot Field

Add a hidden field that bots will fill out but humans won't:

```html
<form id="contact-form">
  <!-- Honeypot - hide this with CSS -->
  <div style="position: absolute; left: -9999px;">
    <label for="website">Website</label>
    <input type="text" name="website" id="website" tabindex="-1" autocomplete="off">
  </div>
  
  <!-- Real fields -->
  <input type="text" name="fi-sender-fullName" required>
  <input type="email" name="fi-sender-email" required>
  <textarea name="fi-text-message" required></textarea>
  
  <button type="submit">Send</button>
</form>

<script>
form.addEventListener('submit', async function(event) {
  event.preventDefault();
  
  // Check honeypot
  const honeypot = document.getElementById('website');
  if (honeypot.value) {
    // Bot detected - silently reject
    statusDiv.textContent = 'Thank you for your message!';
    statusDiv.className = 'success';
    return;
  }
  
  // Continue with real submission...
});
</script>
```

See the [Honeypot documentation](https://forminit.com/docs/honeypot/) for more details.

### Option 2: Cloudflare Turnstile, reCAPTCHA, or hCaptcha

Forminit supports CAPTCHA integration:

- [Cloudflare Turnstile Integration](https://forminit.com/docs/cloudflare-turnstile/)
- [reCAPTCHA Integration](https://forminit.com/docs/recaptcha/)
- [hCaptcha Integration](https://forminit.com/docs/hcaptcha/)

---

## Viewing Submissions

All form submissions appear in your Forminit dashboard at [forminit.com](https://forminit.com):

- View individual submissions
- Search and filter
- Export to CSV
- Set up email notifications
- Configure webhooks for integrations

---

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