Skip to content

Add forms to Neocities

Neocities 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 Neocities account with a website
  • A Forminit account and Form ID

  1. Sign up or log in at 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

Create or edit your HTML file on Neocities:

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

Find this line in the code:

const FORM_ID = 'YOUR_FORM_ID';

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

  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

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

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

FieldName AttributeDescription
Emailfi-sender-emailSubmitter’s email address
First Namefi-sender-firstNameFirst name
Last Namefi-sender-lastNameLast name
Full Namefi-sender-fullNameFull name (use instead of first/last)
Phonefi-sender-phonePhone number (E.164 format)
Companyfi-sender-companyCompany name
Positionfi-sender-positionJob title

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

For additional data, use the appropriate block type:

TypePatternExampleUse For
Textfi-text-{name}fi-text-messageMessages, comments
Numberfi-number-{name}fi-number-quantityQuantities, amounts
Emailfi-email-{name}fi-email-referralAdditional emails
Phonefi-phone-{name}fi-phone-emergencyAdditional phones
URLfi-url-{name}fi-url-websiteLinks
Datefi-date-{name}fi-date-appointmentDates (ISO 8601)
Ratingfi-rating-{name}fi-rating-satisfactionRatings (1-5)
Selectfi-select-{name}fi-select-categoryDropdowns, radios
Countryfi-country-{name}fi-country-shippingCountry codes
Filefi-file-{name}fi-file-attachmentFile uploads

For a complete reference, see the Form Blocks documentation.


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

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

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

When a form is submitted successfully, Forminit returns:

{
  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:

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

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

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;

Forminit automatically validates:

Field TypeValidation
EmailValid email format
PhoneE.164 format (+12025550123)
URLValid URL format
RatingMust be 1-5
CountryISO 3166-1 alpha-2 code
DateISO 8601 format

Add HTML5 validation attributes for better user experience:

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

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

<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 for more details.

Option 2: Cloudflare Turnstile, reCAPTCHA, or hCaptcha

Section titled “Option 2: Cloudflare Turnstile, reCAPTCHA, or hCaptcha”

Forminit supports CAPTCHA integration:


All form submissions appear in your Forminit dashboard at forminit.com:

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

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

View this page as Markdown