Forms
Build forms with validation, CSRF protection, and seamless integration with channels and customers
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:
{% 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:
{% input 'name', label: 'Full Name', required: true %}
{% input 'email', as: 'email', placeholder: 'your@email.com' %}
{% input 'phone', as: 'tel', pattern: '[0-9]{10}' %}Input Types (as parameter):
text(default)emailtelurlnumberdatetimedatetime-localpasswordhidden
{% text_area %}
Multi-line text input:
{% text_area 'message', rows: 5, label: 'Your Message' %}
{% text_area 'bio', rows: 10, placeholder: 'Tell us about yourself...' %}{% select %}
Dropdown selection:
{% select 'category', options: 'Option 1,Option 2,Option 3', label: 'Category' %}
{% select 'country', options: countries, label: 'Country' %}{% checkbox %}
Single checkbox:
{% checkbox 'subscribe', label: 'Subscribe to newsletter' %}
{% checkbox 'terms', label: 'I agree to the terms', required: true %}{% file_field %}
File upload:
{% file_field 'photo', label: 'Upload Photo', accept: 'image/*' %}
{% file_field 'document', label: 'Attach File', accept: '.pdf,.doc,.docx' %}{% submit_tag %}
Submit button:
{% submit_tag 'Send Message' %}
{% submit_tag 'Save', class: 'btn btn-primary btn-lg' %}Validation and Errors
{% error_messages_for %}
Display validation errors:
{% 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:
{% error_messages_for form_model, class: 'alert alert-danger' %}Practical Examples
Contact Form
<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
<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
<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
<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: 'your@email.com', required: true %}
{% submit_tag 'Subscribe', class: 'btn btn-primary' %}
</div>
{% endform %}
</div>
</section>Event Registration with Multiple Attendees
<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
<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
{% 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
{% 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:
<!-- ā
Good: Automatic CSRF protection -->
{% form model %}
<!-- form fields -->
{% endform %}2. Required Field Validation
<!-- ā
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
<!-- ā
Good: Descriptive labels -->
{% input 'phone', as: 'tel', label: 'Phone Number (optional)' %}
<!-- ā Bad: No label -->
<input type="text" name="phone">4. Error Handling
<!-- ā
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!
Multilingual & Localization
Build multilingual sites with locale routing, translations, and localized content
Customer Login with External Providers
Let customers sign in with Microsoft, Facebook, SAML, another Nimbu site or JWT SSO, and build the OAuth2 consent screen when your site is the identity provider