Forminit + Neocities Integration Guide
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
Quick Start
Section titled “Quick Start”Step 1: Create a Form in Forminit
Section titled “Step 1: Create a Form in Forminit”- Sign up or log in at forminit.com
- Create a new form from your dashboard
- Copy your Form ID (looks like
frm_abc123xyz) - Important: Set authentication mode to “Public” in Form Settings
Step 2: Add the Form HTML
Section titled “Step 2: Add the Form HTML”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>
Step 3: Replace the Form ID
Section titled “Step 3: Replace the Form ID”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).
Step 4: Upload to Neocities
Section titled “Step 4: Upload to Neocities”- Log in to your Neocities dashboard
- Upload the HTML file (or edit an existing page)
- Visit your page and test the form
Field Naming Convention
Section titled “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)
Section titled “Sender Fields (Submitter Information)”Use these for collecting information about the person filling out the form:
| Field | Name Attribute | Description |
|---|---|---|
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
Section titled “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 |
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.
Examples
Section titled “Examples”Feedback Form with Rating
Section titled “Feedback Form with Rating”<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
Section titled “Newsletter Signup”<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
Section titled “Contact Form with Subject Dropdown”<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
Section titled “File Upload 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>
Complete JavaScript Template
Section titled “Complete JavaScript Template”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>
Response Handling
Section titled “Response Handling”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: "[email protected]"
},
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."
}
}
Redirecting After Submission
Section titled “Redirecting After Submission”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;
Validation
Section titled “Validation”Forminit automatically validates:
| Field Type | Validation |
|---|---|
| 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
Section titled “Client-Side Validation Tips”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>
Spam Protection
Section titled “Spam Protection”Option 1: Honeypot Field
Section titled “Option 1: Honeypot Field”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: reCAPTCHA or hCaptcha
Section titled “Option 2: reCAPTCHA or hCaptcha”Forminit supports CAPTCHA integration:
Viewing Submissions
Section titled “Viewing Submissions”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
Next Steps
Section titled “Next Steps”- View your submissions in the Forminit dashboard
- Set up email notifications for new submissions
- Explore webhook integrations for advanced workflows