Aire Form Development
When to use this skill
Use this skill when creating, modifying, debugging, or styling HTML forms in a Laravel application that uses the glhd/aire package. This includes any work involving form elements, data binding, validation, or form customization.
Overview
Aire is a Laravel form builder with a fluent API. It is accessed via the Aire facade (Galahad\Aire\Support\Facades\Aire). Aire automatically handles CSRF tokens, HTTP method spoofing, old input repopulation, and server-side validation error display.
Opening and Closing Forms
Every form must be opened and closed. Aire uses output buffering between open and close to capture form fields.
{{ Aire::open()->route('users.store') }}
{{-- form fields --}}
{{ Aire::close() }}
Setting the Action
// From a named route (method is inferred automatically):
Aire::open()->route('users.store')
Aire::open()->route('users.update', $user)
// From a URL:
Aire::open()->action('/users')
// Resourceful (auto-detects store vs. update based on model existence):
Aire::open()->resourceful($user)
Aire::open()->resourceful($user, 'admin.users')
HTTP Methods
Aire automatically infers the method from the route. You can also set it explicitly:
Aire::open()->route('users.store') // POST (inferred)
Aire::open()->route('users.update', $user) // PUT (inferred)
Aire::open()->post()
Aire::open()->put()
Aire::open()->patch()
Aire::open()->delete()
For PUT, PATCH, and DELETE, Aire automatically adds a hidden _method field and sets the real method to POST.
Form Encoding
Aire::open()->multipart() // multipart/form-data (required for file uploads) Aire::open()->urlEncoded() // application/x-www-form-urlencoded
Data Binding
Bind an Eloquent model, array, or object to auto-populate form fields:
{{ Aire::open()->route('users.update', $user)->bind($user) }}
{{ Aire::input('name', 'Name') }} {{-- pre-filled with $user->name --}}
{{ Aire::close() }}
Or with resourceful (which calls bind internally):
{{ Aire::open()->resourceful($user) }}
Value precedence (highest to lowest):
- •Explicitly set via
->value() - •Old input from session (after validation failure)
- •Bound data from the model/array/object
Available Form Elements
Text Inputs
Aire::input('name', 'Full Name')
Aire::email('email', 'Email Address')
Aire::password('password', 'Password')
Aire::search('q', 'Search')
Aire::tel('phone', 'Phone')
Aire::url('website', 'Website')
Aire::number('age', 'Age')
Aire::range('rating', 'Rating', 1, 10)
Date and Time Inputs
Aire::date('start_date', 'Start Date')
Aire::dateTime('event_at', 'Event Date/Time')
Aire::dateTimeLocal('local_at', 'Local Date/Time')
Aire::time('start_time', 'Start Time')
Aire::month('birth_month', 'Birth Month')
Aire::week('target_week', 'Target Week')
Other Inputs
Aire::color('theme_color', 'Theme Color')
Aire::file('avatar', 'Avatar')
Aire::hidden('user_id', $user->id)
Textarea
Aire::textArea('bio', 'Biography')
Select
// Options as key => value array:
Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status')
// Timezone select (pre-populated):
Aire::timezoneSelect('timezone', 'Timezone')
Checkboxes
// Single checkbox:
Aire::checkbox('terms', 'I agree to the terms')
// Checkbox group (multiple values):
Aire::checkboxGroup(['red' => 'Red', 'blue' => 'Blue', 'green' => 'Green'], 'colors', 'Favorite Colors')
Radio Buttons
Aire::radioGroup(['sm' => 'Small', 'md' => 'Medium', 'lg' => 'Large'], 'size', 'Size')
Buttons
Aire::submit('Save')
Aire::button('Cancel')
Error Summary
Display a summary of all validation errors at the top of the form:
Aire::summary() // Shows error count Aire::summary()->verbose() // Shows itemized error list
Fluent Element Methods
All elements support chaining. Common methods available on every input element:
Aire::input('name', 'Name')
->id('custom-id') // Set the element ID
->value('default') // Set an explicit value
->required() // HTML required attribute
->disabled() // HTML disabled attribute
->readOnly() // HTML readonly attribute
->placeholder('Enter name') // Placeholder text
->autoComplete('name') // Autocomplete attribute
->autoFocus() // Autofocus attribute
->addClass('custom-class') // Add CSS class
->removeClass('old-class') // Remove CSS class
->data('key', 'value') // data-* attribute
->variant('lg') // Apply a theme variant
->variants('lg primary') // Apply multiple variants
Grouping (Labels, Help Text, Errors)
By default, every form element is wrapped in a "group" that renders a label, the input, help text, and validation errors together. This is controlled by the group_by_default config option.
Aire::input('name')
->label('Full Name') // Set the label text
->helpText('Enter your legal name') // Help text below the input
->withoutGroup() // Render input without the group wrapper
->grouped() // Force grouping (if disabled by default)
->groupClass('mb-4') // Add CSS class to the group wrapper
->groupId('name-group') // Set group wrapper ID
->prepend('$') // Prepend content inside the group
->append('.00') // Append content inside the group
Validation
Server-Side Validation Errors
Aire automatically reads errors from the session (set by Laravel's validate() or FormRequest) and displays them inline within each element's group. No extra configuration needed.
Using a Custom Error Bag
Aire::open()->errorBag('login')
Client-Side Validation
Aire includes optional JavaScript-based client-side validation using the validatorjs library:
// Pass validation rules directly:
Aire::open()
->route('users.store')
->validate([
'name' => 'required|min:3',
'email' => 'required|email',
])
// Or reference a FormRequest class:
Aire::open()
->route('users.store')
->validate(StoreUserRequest::class)
// Disable client-side validation for a specific form:
Aire::open()->withoutValidation()
Alpine.js Integration
Aire can generate x-data and x-model attributes for Alpine.js:
Aire::open()->asAlpineComponent() Aire::open()->asAlpineComponent(['extra_key' => 'value'])
When enabled, each input element will get an x-model attribute matching its name, and the form gets an x-data attribute with JSON-serialized initial values.
Configuration
Publish the config file to customize Aire's behavior:
php artisan vendor:publish --tag=aire-config
This publishes config/aire.php where you can set:
- •
default_classes: CSS classes for each element type - •
variant_classes: Named style variants (e.g.,sm,lg,primary) - •
validation_classes: CSS classes for validation states (none,valid,invalid) - •
group_by_default: Whether elements are grouped by default - •
validate_by_default: Whether client-side validation is on by default
You can also publish and override Blade templates:
php artisan vendor:publish --tag=aire-views
Complete Form Examples
Create Form
{{ Aire::open()->route('posts.store') }}
{{ Aire::summary() }}
{{ Aire::input('title', 'Title')->required() }}
{{ Aire::textArea('body', 'Content')->autoSize() }}
{{ Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status') }}
{{ Aire::submit('Create Post') }}
{{ Aire::close() }}
Edit Form with Model Binding
{{ Aire::open()->resourceful($post) }}
{{ Aire::summary() }}
{{ Aire::input('title', 'Title')->required() }}
{{ Aire::textArea('body', 'Content')->autoSize() }}
{{ Aire::select(['draft' => 'Draft', 'published' => 'Published'], 'status', 'Status') }}
{{ Aire::submit('Update Post') }}
{{ Aire::close() }}
Delete Form
{{ Aire::open()->route('posts.destroy', $post) }}
{{ Aire::submit('Delete Post')->variant('danger') }}
{{ Aire::close() }}
Form with Client-Side Validation
{{ Aire::open()->route('users.store')->validate(StoreUserRequest::class) }}
{{ Aire::summary() }}
{{ Aire::input('name', 'Name')->required() }}
{{ Aire::email('email', 'Email')->required() }}
{{ Aire::password('password', 'Password')->required() }}
{{ Aire::password('password_confirmation', 'Confirm Password')->required() }}
{{ Aire::submit('Register') }}
{{ Aire::close() }}
File Upload Form
{{ Aire::open()->route('avatars.store')->multipart() }}
{{ Aire::file('avatar', 'Profile Photo') }}
{{ Aire::submit('Upload') }}
{{ Aire::close() }}