Forms Guide
Learn how to use RailsKit form components to build accessible, responsive forms with automatic error handling.
Overview
RailsKit form components provide a comprehensive set of inputs designed for modern Rails applications. Each component supports both Rails form builder integration and standalone usage, giving you flexibility in how you build your forms.
- Automatic error detection — Components read errors directly from your form object
- Dark mode support — All components include
dark:variants - Accessibility built-in — Proper labels, ARIA attributes, and keyboard navigation
- Consistent styling — Tailwind-only classes for easy customization
Dual-Mode Pattern
Every form component supports two modes of operation. The component automatically detects which mode to use based on the parameters you provide.
Form Builder Mode
Pass form: and attribute: to integrate with Rails form objects.
Standalone Mode
Pass name: and value: for forms without a backing model.
Form Builder Mode
Use form builder mode when working with Rails models. The component will automatically:
- Generate the correct
nameattribute - Populate the current value from the model
- Display validation errors from the model
- Generate accessible
idattributes for labels
<%= form_with model: @user do |f| %>
<%= render TextFieldComponent.new(
form: f,
attribute: :email,
label: "Email address",
type: "email",
required: true,
hint: "We'll never share your email"
) %>
<%= render TextFieldComponent.new(
form: f,
attribute: :password,
type: "password",
required: true
) %>
<%= render CheckboxComponent.new(
form: f,
attribute: :remember_me,
label: "Remember me",
description: "Stay signed in for 30 days"
) %>
<% end %>
<%= form_with model: @user do |f| %>
<%= render "components/text_field",
form: f,
attribute: :email,
label: "Email address",
type: "email",
required: true %>
<% end %>
Standalone Mode
Use standalone mode for search forms, filters, or any form that doesn't have a backing model.
<%= render TextFieldComponent.new(
name: "search",
placeholder: "Search components...",
type: "search"
) %>
<%= render SelectComponent.new(
name: "category",
label: "Category",
options: ["All", "Navigation", "Forms", "Overlays"],
selected: "All"
) %>
Error Handling
Form components automatically detect and display errors in two ways:
Automatic Detection (Form Builder Mode)
When using form builder mode, components automatically check form.object.errors
for the specified attribute. If errors exist, the component displays error styling and the first error message.
# In your controller
def create
@user = User.new(user_params)
unless @user.save
render :new, status: :unprocessable_entity
end
end
# In your view - errors display automatically!
<%= render TextFieldComponent.new(
form: f,
attribute: :email
) %>
Manual Errors
You can also pass errors manually using the error: parameter.
This is useful for client-side validation or custom error scenarios.
<%= render TextFieldComponent.new(
name: "api_key",
label: "API Key",
error: "Invalid API key format"
) %>
Accessibility
All form components are built with accessibility in mind:
-
Labels — Every input has a properly associated
<label>element -
Required fields — Required fields include both a visual indicator and the
requiredattribute -
Error association — Error messages are connected via
aria-describedby -
Focus states — Clear focus rings using
focus-visible:for keyboard users - Keyboard navigation — Interactive components like Combobox support full keyboard control
<div class="w-full">
<label for="user_email" class="...">
Email
<span class="text-red-500">*</span>
</label>
<input
type="email"
name="user[email]"
id="user_email"
required
aria-describedby="user_email_error"
class="..."
/>
<p id="user_email_error" class="...">
can't be blank
</p>
</div>
Available Components
RailsKit includes a comprehensive set of form components:
Text Field
Single-line text input with label, hint, and error states
Textarea
Multi-line text with auto-resize and character counter
Select
Native dropdown select with option groups
Checkbox
Single checkbox with label and description
Radio Group
Group of mutually exclusive radio options
Checkbox Group
Group of checkboxes for multi-select
Switch
Toggle switch for boolean values
Input Group
Input with prefix/suffix addons
File Upload
Drag-and-drop file upload zone
Combobox
Searchable select with keyboard navigation
Examples
Login Form
<%= form_with url: session_path, method: :post do |f| %>
<%= render TextFieldComponent.new(
name: "email",
label: "Email",
type: "email",
required: true,
placeholder: "[email protected]"
) %>
<%= render TextFieldComponent.new(
name: "password",
label: "Password",
type: "password",
required: true
) %>
<div class="flex items-center justify-between">
<%= render CheckboxComponent.new(
name: "remember_me",
label: "Remember me"
) %>
<%= link_to "Forgot password?", "#", class: "text-sm text-slate-600 hover:text-slate-900" %>
</div>
<button type="submit" class="w-full ...">
Sign in
</button>
<% end %>
Settings Form
<%= form_with model: @settings do |f| %>
<%= render TextFieldComponent.new(
form: f,
attribute: :display_name,
label: "Display name",
hint: "This is how your name will appear to others"
) %>
<%= render TextareaComponent.new(
form: f,
attribute: :bio,
label: "Bio",
rows: 4,
max_length: 500,
hint: "Tell us about yourself"
) %>
<%= render ComboboxComponent.new(
form: f,
attribute: :timezone,
label: "Timezone",
options: ActiveSupport::TimeZone.all.map { |tz| [tz.name, tz.tzinfo.name] },
placeholder: "Select your timezone..."
) %>
<%= render SwitchComponent.new(
form: f,
attribute: :email_notifications,
label: "Email notifications",
description: "Receive email updates about your account"
) %>
<%= render SwitchComponent.new(
form: f,
attribute: :marketing_emails,
label: "Marketing emails",
description: "Receive tips and product announcements"
) %>
<% end %>
Contact Form
<%= form_with url: contact_path, method: :post do |f| %>
<div class="grid grid-cols-2 gap-4">
<%= render TextFieldComponent.new(
name: "first_name",
label: "First name",
required: true
) %>
<%= render TextFieldComponent.new(
name: "last_name",
label: "Last name",
required: true
) %>
</div>
<%= render InputGroupComponent.new(
name: "email",
label: "Email",
type: "email",
trailing_addon: "@company.com",
required: true
) %>
<%= render SelectComponent.new(
name: "subject",
label: "Subject",
options: ["General inquiry", "Technical support", "Sales", "Partnership"],
include_blank: "Select a subject..."
) %>
<%= render TextareaComponent.new(
name: "message",
label: "Message",
rows: 6,
required: true,
placeholder: "How can we help you?"
) %>
<%= render FileUploadComponent.new(
name: "attachments",
label: "Attachments",
multiple: true,
accept: ".pdf,.doc,.docx,.png,.jpg",
hint: "Upload any relevant files (max 10MB each)"
) %>
<% end %>