Open-source project
glhd/aire avatar
glhd/aire

Aire: a Laravel form builder that renders itself from a Blade facade

Modern form builder for Laravel

543 stars41 forksPHPMIT

At a glance

What is it?
Aire is a modern form builder for Laravel that draws its inputs, labels, help text and error display through a fluent facade or through x-aire Blade components, and it handles the parts every form needs and nobody enjoys: old input after a validation failure, CSRF tokens, method spoofing and binding an Eloquent model. It suits teams already on Laravel and Tailwind who want forms to look consistent without hand-writing markup per field.
Who is it for?
Adopt Aire if your application is on Laravel 5.8.28 or newer with PHP 7.1 or newer, uses Tailwind, and has forms spread across enough templates that hand-written markup has become inconsistent. Do not adopt it if you need rich select widgets, since support for Choices.js is still only an idea on the project's list, and do not rely on the client-side validation until the browser testing improves.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 72 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A fluent facade for forms in Blade templates

Aire is a modern Laravel form builder whose stated focus is the same expressive and beautiful code you expect from the Laravel ecosystem. The usual way to use it is through the Aire facade inside a Blade template, where every method call is fluent so each component can be configured as it is written.

A form is opened, given a route and a bound object, filled with fields, and closed:

php
{{ Aire::open()
  ->route('users.update')
  ->bind($user) }}

{{ Aire::input('given_name', 'First/Given Name')
    ->id('given_name') }}
    
{{ Aire::input('family_name', 'Last/Family Name')
    ->id('family_name')
    ->autoComplete('off') }}
  
{{ Aire::email('email', 'Email Address')
    ->helpText('Please use your company email address.') }}
  
{{ Aire::submit('Update User') }}
  
{{ Aire::close() }}

The input type is part of the method name, so Aire::email produces an email field and Aire::input produces a text field, and the second argument is the label. Options that Bootstrap and Laravel developers expect are chainable, including id, autoComplete and helpText.

The value of the facade is that the label, the help text, the error styling and the association between an error message and its field are all handled by the library. The alternative, and the one this project is measured against, is a Bootstrap form template repeated across views, where each hand-written input needs its own error class and its own message block.

Blade components as an alternative to the facade

As of Aire 2.4.0, every Aire element is also available as a Blade component, and the README states that the form above is identical when written this way:

php
<x-aire::form route="users.update" :bind="$user">

    <x-aire::input 
        name="given_name" 
        label="First/Given Name" 
        id="given_name" 
    />
    <x-aire::input 
        name="family_name" 
        label="Last/Family Name" 
        id="family_name" 
        auto-complete="off" 
    />
    <x-aire::email 
        name="email" 
        label="Email Address" 
        help-text="Please use your company email address." 
    />
    
    <x-aire::submit label="Update User" />
    
</x-aire::form>

The difference is not cosmetic. The facade approach puts the form logic in a PHP expression, which keeps the call chain and its configuration in one place. The component approach puts markup in markup, which is easier to read for anyone who thinks in templates and easier to open in an editor with syntax highlighting.

Note the naming difference between the two forms of the same option. The facade uses camelCase, so autoComplete('off'), while the component uses kebab-case, so auto-complete="off". That is a small inconsistency, but it is the kind of detail that catches people moving a form from one style to the other.

Installing Aire and rendering a first form

Installation is a single Composer command, and the README gives nothing else to do:

bash
composer require glhd/aire

That is the whole setup. The package ships its own views and its own JavaScript, so there is no asset pipeline step, no publish command required before use, and no service provider to register by hand. After that you can write a form in a Blade template with the Aire facade and it will render.

A first real use, then, is the facade form shown earlier, placed in a view that already receives the object you want to bind. The chain of calls that matters at the start is open, then route, then bind, then the fields, then close, in that order. Aire::open() starts the form element and Aire::close() closes it, and the fields in between are independent of each other, so the order of the fields does not matter.

The demo at airephp.com shows the same surface with real data, which is the quickest way to see what the default Tailwind styling looks like before you commit to it. The styling is worth looking at early because it is the thing you will either keep or replace, as the next section explains.

Binding data, and the order values are applied in

Aire binds old input to your form automatically, so values survive a validation failure without you re-reading the session. You can also bind data explicitly, and the bind() method accepts an Eloquent model, an array, or any object:

php
// Bind Eloquent models
Aire::bind(User::find(1));

// Bind an array
Aire::bind(['given_name' => 'Chris']);

// Bind any object
Aire::bind((object) ['given_name' => 'Chris']);

Because it takes any object, the binding does not require a specific model class, which matters when a form edits something that is not an Eloquent record.

The precedence rules are where a lot of the value sits, and the README states them as an ordered list. Values set with value() are applied no matter what. Old input is applied if it is available. Bound data is applied last.

Read in that order, it means the explicit value wins over everything, and the bound model is only a fallback that old input overrides. That ordering is what makes the automatic old input behaviour safe: a user who typed something, submitted and hit a validation error sees their own input rather than the stale values from the database, while a first visit to the form shows the model.

Method spoofing, route inference and the CSRF field

Two things that every form needs are handled automatically, and both are the kind of detail that is easy to leave out in a hand-rolled template.

The first is method spoofing. Aire adds the Laravel _method field for forms that are neither GET nor POST, and it also infers the intended method from the route where it can. Given a named route in routes/web.php:

php
// In routes
Route::delete('/photos/{photo}', 'PhotosController@destroy')
	->name('photos.destroy');

// In your view
{{ Aire::open()->route('photos.destroy', $photo) }}
{{ Aire::close() }}

the rendered output is a POST form with a hidden field carrying the real verb, which is the standard Laravel mechanism for browsers that only speak GET and POST. The route inference is the useful part in practice: naming the route is enough, and the method follows from the route definition, so a delete route does not need a separate method argument on the form.

The second is the CSRF token. Aire injects it automatically if one exists and the form is not a GET form, so with the session enabled a hidden _token field appears without you writing it. Both behaviours are quiet, correct defaults rather than configurable options, which is the right choice for things that are almost always wanted.

Server-side errors and early-stage client-side validation

If you validate on the server, Aire picks up the errors and applies error classes and shows the message within the associated input group, so the error message appears where the field is rather than in a separate block you have to position by hand. There is also a summary helper for the top of the page:

php
// Print "There are X errors on this page that you must fix before continuing."
{{ Aire::summary() }}

// Also include an itemized list of errors
{{ Aire::summary()->verbose() }}

The summary without verbose gives the count, and the verbose version adds the itemized list, which is a sensible pair for a form with many fields where a single message at the top is not enough.

Client-side validation is where the project is candid about its limits. The README says JavaScript validation in Aire is in its early stages, that browser testing is limited, and that the JavaScript code has not had performance optimizations applied. Even so, passing an array of rules or a FormRequest object makes Aire apply most rules on the client side, and the credit for that goes to validatorjs, which appears in the project's package.json as a dependency.

So the client-side layer is a convenience that removes round trips for the common cases, not something to rely on. Server-side validation remains the one that decides the outcome, and the JavaScript is best treated as an accelerator for the user rather than a guarantee.

Changing the markup through config, or taking over the views

Aire ships with classes that work with the default Tailwind names out of the box, and the README notes the defaults as .bg-blue-600 and similar. To change the classes for any element there are two routes, and the difference between them is a maintenance decision.

The first is the config file, published with php artisan vendor:publish --tag=aire-config, where you update the default_classes entry for the element you care about:

php
return [
  'default_classes' => [
    'input' => 'text-gray-900 bg-white border rounded-sm',
  ],
];

The same config file holds a handful of other options and is documented throughout, so publishing it is worth doing early even if you change nothing.

The second route is publishing the views themselves with php artisan vendor:publish --tag=aire-views, which gives you total control over rendering. There is a view file per component type, such as input.blade.php, plus files for component grouping.

The README states the trade-off plainly: published views give the most flexibility, but mean that you have to maintain your views as Aire releases add features or change component rendering. That is the real cost. A class name change is absorbed by the config file, while a rendering change is absorbed by a fork of the views. For a project that intends to stay on the package across many releases, keeping the config route open is usually the cheaper path, and treating published views as a last resort.

Version support, the licence and the gap between releases

Aire should run on Laravel 5.8.28 and higher, and PHP 7.1 and higher. The stated policy is to test the last two major releases of PHP and Laravel, so support below that is not guaranteed. That policy is the thing to watch: the package tracks the Laravel versions you are likely on, and drops the older ones as the supported window moves.

The licence is MIT, and the repository carries a LICENSE file alongside the CHANGELOG.md, a docs/ directory, tests/, translations/, js/, resources/, config/ and a bin/ directory. The JavaScript side is built with rollup and tested with ava, with puppeteer in the development dependencies for browser-level checks.

On versions, the listed releases are 2.15.1 on 2026-02-14, 2.15.2 on 2026-03-02 and 2.16.0 on 2026-03-23, and the last push to the repository was on 2026-08-13. The gap between the newest published release and the last commit means the main branch is ahead of the tagged version, so composer require gives you the latest tagged release while a git checkout gives you work that has not been packaged.

The project also publishes a list of things that are in the works or under consideration: read-only plain text, cross-browser support for custom checkboxes and radio buttons through a config option, support for Choices.js or similar select UI libraries, better handling of file inputs, and better support for prepending or appending content to inputs. None of these exist yet, and the select widget entry is the one most likely to matter, since a project that wants a styled select control cannot get it from Aire today.

Editorial conclusion

Adopt Aire if your application is on Laravel 5.8.28 or newer with PHP 7.1 or newer, uses Tailwind, and has forms spread across enough templates that hand-written markup has become inconsistent. Do not adopt it if you need rich select widgets, since support for Choices.js is still only an idea on the project's list, and do not rely on the client-side validation until the browser testing improves. Before you commit, install the package with composer require glhd/aire, publish the config with php artisan vendor:publish --tag=aire-config to see the default_classes and other options in full, and check whether the views you would need to maintain yourself, since publishing custom views means you own the rendering as Aire changes. The MIT licence is permissive with no obligations beyond keeping the notice, the last release was 2.16.0 on 2026-03-23, and the last push was on 2026-08-13.

Frequently asked questions

How do I install Aire in a Laravel project?

Run composer require glhd/aire. That is the whole installation, since the package ships its own views and JavaScript and does not require a publish step or a manually registered service provider before use.

What is the difference between the Aire facade and the x-aire Blade components?

Both render the same form. The facade form uses fluent method calls such as Aire::open() and Aire::input('given_name', 'First/Given Name'), while the component form uses x-aire::form, x-aire::input and x-aire::submit tags. Blade components are available as of Aire 2.4.0.

How does Aire handle old input and binding precedence?

Aire binds old input automatically so values survive a validation error, and you can also call bind() with an Eloquent model, an array or any object. Precedence is: values set with value() are applied no matter what, old input is applied if available, and bound data is applied last.

Which Laravel and PHP versions does Aire support?

The README says Aire should run on Laravel 5.8.28 and higher, and PHP 7.1 and higher, and that the policy is to test the last two major releases of PHP and Laravel, so support below that is not guaranteed.

How do I change the default Tailwind classes Aire uses?

Publish the config file with php artisan vendor:publish --tag=aire-config and update the default_classes entry for the element. For full control over rendering, publish the views with php artisan vendor:publish --tag=aire-views, which means you maintain those views yourself as Aire changes.

Does Aire add the CSRF token and handle delete and put routes?

Yes. Aire injects a CSRF token automatically when one exists and the form is not a GET form, and it adds the Laravel _method field for forms that are neither GET nor POST, inferring the method from the named route where it can.

Official sources

  1. glhd/aire on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/glhd-aire.svg)](https://hysenlabs.com/projects/glhd-aire)

Was this useful?