Forms
Nimbu provides a comprehensive form system with automatic CSRF protection, validation, file uploads, and integration with channels and customer accounts.
Basic Form Structure
{% form %}
Create forms for any content type:
liquid
{% form model %}
{% input 'field_name' %}
{% submit_tag 'Save' %}
{% endform %}Supported Models:
channels.channel_name- Channel entry formscustomer- Customer account formsorder- Order/checkout formspage- Page editable forms (with authentication)
Form Tags
{% input %}
Generate input fields with automatic attributes:
liquid
{% input 'name', label: 'Full Name', required: true %}
{% input 'email', as: 'email', placeholder: '[email protected]' %}
{% input 'phone', as: 'tel', pattern: '[0-9]{10}' %}Input Types (as parameter):
text(default)emailtelurlnumberdatetimedatetime-localpasswordhidden
{% text_area %}
Multi-line text input:
liquid
{% text_area 'message', rows: 5, label: 'Your Message' %}
{% text_area 'bio', rows: 10, placeholder: 'Tell us about yourself...' %}{% select %}
Dropdown selection:
liquid
{% select 'category', options: 'Option 1,Option 2,Option 3', label: 'Category' %}
{% select 'country', options: countries, label: 'Country' %}{% checkbox %}
Single checkbox:
liquid
{% checkbox 'subscribe', label: 'Subscribe to newsletter' %}
{% checkbox 'terms', label: 'I agree to the terms', required: true %}{% file_field %}
File upload:
liquid
{% file_field 'photo', label: 'Upload Photo', accept: 'image/*' %}
{% file_field 'document', label: 'Attach File', accept: '.pdf,.doc,.docx' %}{% submit_tag %}
Submit button:
liquid
{% submit_tag 'Send Message' %}
{% submit_tag 'Save', class: 'btn btn-primary btn-lg' %}Validation and Errors
{% error_messages_for %}
Display validation errors:
liquid
{% form channels.contact %}
{% error_messages_for form_model %}
{% input 'name', required: true %}
{% input 'email', as: 'email', required: true %}
{% text_area 'message', required: true %}
{% submit_tag 'Send' %}
{% endform %}Custom Error Styling:
liquid
{% error_messages_for form_model, class: 'alert alert-danger' %}Practical Examples
Contact Form
liquid
<div class="contact-page">
<h2>Get in Touch</h2>
{% form channels.contact, class: 'contact-form' %}
{% error_messages_for form_model %}
<div class="form-row">
<div class="col-md-6">
{% input 'first_name', label: 'First Name', required: true %}
</div>
<div class="col-md-6">
{% input 'last_name', label: 'Last Name', required: true %}
</div>
</div>
<div class="form-group">
{% input 'email', as: 'email', label: 'Email Address', required: true %}
</div>
<div class="form-group">
{% input 'phone', as: 'tel', label: 'Phone Number' %}
</div>
<div class="form-group">
{% select 'subject', options: 'General Inquiry,Support,Sales,Partnership', label: 'Subject', required: true %}
</div>
<div class="form-group">
{% text_area 'message', rows: 6, label: 'Message', required: true %}
</div>
<div class="form-group">
{% checkbox 'subscribe', label: 'Subscribe to our newsletter' %}
</div>
{% submit_tag 'Send Message', class: 'btn btn-primary' %}
{% endform %}
</div>Job Application Form
liquid
<div class="job-application">
<h2>Apply for {{ job.title }}</h2>
{% form channels.applications, class: 'application-form' %}
{% error_messages_for form_model %}
<input type="hidden" name="job_id" value="{{ job.id }}">
<h3>Personal Information</h3>
<div class="form-row">
<div class="col-md-6">
{% input 'first_name', label: 'First Name', required: true %}
</div>
<div class="col-md-6">
{% input 'last_name', label: 'Last Name', required: true %}
</div>
</div>
<div class="form-group">
{% input 'email', as: 'email', label: 'Email', required: true %}
</div>
<div class="form-group">
{% input 'phone', as: 'tel', label: 'Phone Number', required: true %}
</div>
<div class="form-group">
{% input 'linkedin', as: 'url', label: 'LinkedIn Profile URL' %}
</div>
<h3>Application Details</h3>
<div class="form-group">
{% file_field 'resume', label: 'Resume (PDF)', accept: '.pdf', required: true %}
</div>
<div class="form-group">
{% file_field 'cover_letter', label: 'Cover Letter (PDF)', accept: '.pdf' %}
</div>
<div class="form-group">
{% text_area 'why_you', rows: 6, label: 'Why do you want to work with us?', required: true %}
</div>
<div class="form-group">
{% input 'start_date', as: 'date', label: 'Available Start Date' %}
</div>
<div class="form-group">
{% checkbox 'gdpr_consent', label: 'I consent to the processing of my personal data', required: true %}
</div>
{% submit_tag 'Submit Application', class: 'btn btn-lg btn-primary' %}
{% endform %}
</div>Customer Registration
liquid
<div class="registration-page">
<h2>Create Account</h2>
{% form customer, class: 'registration-form' %}
{% error_messages_for form_model %}
<div class="form-group">
{% input 'email', as: 'email', label: 'Email Address', required: true %}
</div>
<div class="form-group">
{% input 'password', as: 'password', label: 'Password', required: true %}
</div>
<div class="form-group">
{% input 'password_confirmation', as: 'password', label: 'Confirm Password', required: true %}
</div>
<div class="form-row">
<div class="col-md-6">
{% input 'first_name', label: 'First Name', required: true %}
</div>
<div class="col-md-6">
{% input 'last_name', label: 'Last Name', required: true %}
</div>
</div>
<div class="form-group">
{% checkbox 'newsletter', label: 'Send me news and special offers' %}
</div>
<div class="form-group">
{% checkbox 'terms', label: 'I agree to the Terms and Conditions', required: true %}
</div>
{% submit_tag 'Create Account', class: 'btn btn-primary btn-block' %}
{% endform %}
<p class="login-link">
Already have an account? <a href="/login">Sign in</a>
</p>
</div>Newsletter Signup
liquid
<section class="newsletter-signup">
<div class="container">
<h3>Stay Updated</h3>
<p>Get the latest news and exclusive offers.</p>
{% form channels.newsletter, class: 'newsletter-form' %}
{% error_messages_for form_model %}
<div class="form-inline">
{% input 'email', as: 'email', placeholder: '[email protected]', required: true %}
{% submit_tag 'Subscribe', class: 'btn btn-primary' %}
</div>
{% endform %}
</div>
</section>Event Registration with Multiple Attendees
liquid
<div class="event-registration">
<h2>Register for {{ event.title }}</h2>
{% form channels.registrations, class: 'registration-form' %}
{% error_messages_for form_model %}
<input type="hidden" name="event_id" value="{{ event.id }}">
<h3>Primary Attendee</h3>
<div class="form-row">
<div class="col-md-6">
{% input 'first_name', label: 'First Name', required: true %}
</div>
<div class="col-md-6">
{% input 'last_name', label: 'Last Name', required: true %}
</div>
</div>
<div class="form-group">
{% input 'email', as: 'email', label: 'Email', required: true %}
</div>
<div class="form-group">
{% input 'company', label: 'Company Name' %}
</div>
<h3>Ticket Information</h3>
<div class="form-group">
{% select 'ticket_type', options: 'Standard,VIP,Student', label: 'Ticket Type', required: true %}
</div>
<div class="form-group">
{% input 'quantity', as: 'number', label: 'Number of Tickets', value: '1', min: '1', max: '10', required: true %}
</div>
<h3>Dietary Requirements</h3>
<div class="form-group">
{% checkbox 'vegetarian', label: 'Vegetarian' %}
{% checkbox 'vegan', label: 'Vegan' %}
{% checkbox 'gluten_free', label: 'Gluten-free' %}
</div>
<div class="form-group">
{% text_area 'special_requests', rows: 3, label: 'Special Requests or Allergies' %}
</div>
{% submit_tag 'Complete Registration', class: 'btn btn-lg btn-success' %}
{% endform %}
</div>Product Review Form
liquid
<div class="review-form">
<h3>Write a Review</h3>
{% form channels.reviews, class: 'product-review-form' %}
{% error_messages_for form_model %}
<input type="hidden" name="product_id" value="{{ product.id }}">
<div class="form-group">
<label>Rating *</label>
<div class="rating-input">
{% for i in (1..5) %}
<input type="radio" name="rating" id="rating-{{ i }}" value="{{ i }}" required>
<label for="rating-{{ i }}">★</label>
{% endfor %}
</div>
</div>
<div class="form-group">
{% input 'title', label: 'Review Title', required: true %}
</div>
<div class="form-group">
{% text_area 'comment', rows: 5, label: 'Your Review', required: true %}
</div>
<div class="form-group">
{% input 'reviewer_name', label: 'Your Name', required: true %}
</div>
<div class="form-group">
{% input 'reviewer_email', as: 'email', label: 'Email (not published)', required: true %}
</div>
<div class="form-group">
{% checkbox 'verified_purchase', label: 'I purchased this product' %}
</div>
{% submit_tag 'Submit Review', class: 'btn btn-primary' %}
{% endform %}
</div>Advanced Form Patterns
Conditional Fields
liquid
{% form channels.bookings %}
{% select 'booking_type', options: 'Individual,Group', label: 'Booking Type' %}
<div class="conditional-fields" data-show-if="booking_type:Group">
{% input 'group_name', label: 'Group Name' %}
{% input 'group_size', as: 'number', label: 'Number of People' %}
</div>
{% submit_tag 'Book Now' %}
{% endform %}
<script>
document.querySelector('[name="booking_type"]').addEventListener('change', function(e) {
const groupFields = document.querySelector('[data-show-if="booking_type:Group"]');
groupFields.style.display = e.target.value === 'Group' ? 'block' : 'none';
});
</script>AJAX Form Submission
liquid
{% form channels.contact, class: 'ajax-form', id: 'contact-form' %}
{% input 'name', required: true %}
{% input 'email', as: 'email', required: true %}
{% text_area 'message', required: true %}
<div class="form-status"></div>
{% submit_tag 'Send Message' %}
{% endform %}
<script>
document.getElementById('contact-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const statusDiv = this.querySelector('.form-status');
try {
const response = await fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json'
}
});
if (response.ok) {
statusDiv.innerHTML = '<div class="alert alert-success">Message sent successfully!</div>';
this.reset();
} else {
const errors = await response.json();
statusDiv.innerHTML = `<div class="alert alert-danger">${errors.message}</div>`;
}
} catch (error) {
statusDiv.innerHTML = '<div class="alert alert-danger">An error occurred. Please try again.</div>';
}
});
</script>Best Practices
1. CSRF Protection
All Nimbu forms automatically include CSRF tokens. Never disable this:
liquid
<!-- ✅ Good: Automatic CSRF protection -->
{% form model %}
<!-- form fields -->
{% endform %}2. Required Field Validation
liquid
<!-- ✅ Good: Use required attribute -->
{% input 'email', as: 'email', required: true %}
<!-- ✅ Good: Client + server validation -->
{% input 'age', as: 'number', min: '18', required: true %}3. Accessible Labels
liquid
<!-- ✅ Good: Descriptive labels -->
{% input 'phone', as: 'tel', label: 'Phone Number (optional)' %}
<!-- ❌ Bad: No label -->
<input type="text" name="phone">4. Error Handling
liquid
<!-- ✅ Good: Show errors clearly -->
{% form model %}
{% error_messages_for form_model, class: 'alert alert-danger' %}
<!-- form fields -->
{% endform %}Next Steps
- Channels - Channel entry forms
- Commerce - Shopping cart forms
- Global Variables - Form drops reference
- Advanced Features - OAuth, integrations
Build powerful, accessible forms with Nimbu!