spatie/laravel-html

repository·main·Indexed 21 days ago

https://github.com/spatie/laravel-html

A package for painless, fluent HTML generation in Laravel. It provides a clean API for building dynamic HTML elements and form components that integrate with Laravel's request and session state. The library features an immutable element system, a central Html builder for context-aware element creation, and support for custom HTML elements and extended builder methods.

Tokens
14.7K
Snippets
60
Records
68
Agent score
73%

What's inside spatie/laravel-html

  1. How elements and immutability work

    main

    HTML elements (found in the Spatie\Html\Elements namespace) are typically created using a Spatie\Html\Html builder instance.

    Immutability: Element instances are immutable. When you call a fluent method to modify attributes or contents, it returns a new instance rather than modifying the existing one. To capture changes, you must assign the result to a variable.

    $icon = html()->span()->class('fa');
    
    // This returns a NEW instance with both classes applied
    $icon->class('fa-eye'); // '<span class="fa fa-eye"></span>'
    
    // This returns a NEW instance with the slash class applied
    $icon->class('fa-eye-slash'); // '<span class="fa fa-eye-slash"></span>'
    html()->span()->text('Hello world!');
  2. Use models to automatically populate form values

    main

    The HTML builder can bind to a model to automatically populate input values. By calling html()->model($model), subsequent input elements will automatically pull their values from the provided model's properties.

    A "model" can be any object that implements ArrayAccess, such as an Eloquent model or a plain associative array.

    {{ html()->model($user) }}
    
    {{ html()->input('name') }}
  3. How element methods work in Spatie Laravel HTML

    main

    All Spatie\Html\Elements provide a fluent API for building HTML. Methods can be chained together, and because elements are immutable, every method returns a new instance of the Element. This allows you to preserve the original element if needed.

    Additionally, you can use the If suffix on any method (e.g., classIf(), attributeIf()) to execute that method only if the first parameter is true.

    echo Div::classIf(true, 'row');
    // "<div class="row"></div>"
    
    echo Div::classIf(false, 'row');
    // "<div></div>"
    
    echo Div::attributeIf(50 > 100, 'data-custom', 'Attribute value');
    // "<div></div>"
  4. Difference between builder methods and element methods

    main

    The package distinguishes between the builder (which handles external state) and elements (which are deterministic).

    1. Builder Methods: These can be non-deterministic. They can pull values from the session, the request, or a model to automatically populate form elements (e.g., handling old input after a validation failure).
    2. Element Methods: These are deterministic. They only care about the values explicitly passed to them and have no knowledge of the outside world (like requests or sessions).

    Example:

    // The builder method 'email()' attempts to resolve an initial value 
    // from the session/request, falling back to the provided default.
    $email = html()->email('email', 'hello@example.com');
    
    // The element method 'value()' is deterministic and will 
    // always set the value to exactly what you provide.
    $email = html()->email('email')->value('hello@example.com');
    // This will try to resolve an initial value, and fall back to 'hello@example.com'
    $email = html()->email('email', 'hello@example.com');
    
    // This will always have 'hello@example.com' as its value
    $email = html()->email('email')->value('hello@example.com');
  5. Couple the HTML builder to a model

    main

    You can link the HTML builder to an Eloquent model using the model() method. When a model is active, form fields will automatically attempt to use the values from the corresponding model attributes for their value attribute.

    To stop using the current model for subsequent elements, use the endModel() method.

    $user = new User(['name' => 'Johnny']);
    html()->model($user);
    echo html()->text('name');
    // Outputs: <input type="text" name="name" value="Johnny">
    $user = new User(['name' => 'Johnny']);
    html()->model($user);
    echo html()->text('name');
    // Outputs: <input type="text" name="name" value="Johnny">
  6. Distinguish between Builder Params and Element Methods

    main

    When using the Html builder with models, there is a distinction between parameters passed to the builder and methods called on the resulting element.

    If you pass a value as a parameter to a builder method (like text('name', 'Alex')), the builder may prioritize context from a bound model over your provided value. To ensure a specific value is used regardless of model context, create the element via the builder and then chain the value method onto the element itself.

    Example: Overriding model values

    // The builder infers 'Sebastian' from the model, ignoring 'Alex'
    {{ html()->model(new User(['name' => 'Sebastian'])) }}
    {{ html()->text('name', 'Alex') }}
    // <input type="text" name="name" id="name" value="Sebastian" />
    
    // By chaining ->value() on the element, you overwrite the model's value
    {{ html()->model(new User(['name' => 'Sebastian'])) }}
    {{ html()->input('name')->value('Alex') }}
    // <input type="text" name="name" id="name" value="Alex" />
    {{ html()->model(new User(['name' => 'Sebastian'])) }}
    {{ html()->text('name', 'Alex') }}
    // <input type="text" name="name" id="name" value="Sebastian" />
    
    {{ html()->model(new User(['name' => 'Sebastian'])) }}
    {{ html()->input('name')->value('Alex') }}
    // <input type="text" name="name" id="name" value="Alex" />
  7. Understand the relationship between the Html builder and Element classes

    main

    The package is built around two main components:

    1. Spatie\™\Html\\Elements classes: These represent individual HTML elements. They are 'stateless' and have no knowledge of the outside world (like requests or models) on their own.
    2. Spatie\Html\Html builder: This is the central orchestrator. It uses builder methods to create Element instances. Crucially, the builder provides context by coupling elements to Laravel's requests, sessions, and models (e.g., automatically pulling 'old' input values or model attributes).

    You can chain fluent methods from the Element classes directly onto the Html builder methods because the builder returns Element instances.

  8. Register an extended Html class in AppServiceProvider

    main

    To make your custom Html class available throughout your application via the package's standard injection or helpers, register it as a singleton in your AppServiceProvider (or any other service provider). You must bind Spatie\Html\Html::class to your custom class implementation.

    <?php
    
    namespace App\Providers;
    
    use App\Services\HtmlExtended;
    use Illuminate\Support\ServiceProvider;
    use Spatie\Html\Html;
    
    class AppServiceProvider extends ServiceProvider
    {
        /**
         * Register any application services.
         */
        public function register(): void
        {
            $this->app->singleton(Html::class, HtmlExtended::class);
        }
    }
  9. Register the HtmlServiceProvider

    main

    After installing, you must register the Spatie\Html\HtmlServiceProvider::class in your application configuration to enable the package features.

    // For Laravel versions prior to 11, add to config/app.php
    'providers' => [
        ...
        Spatie\Html\HtmlServiceProvider::class,
    ];
    
    // For Laravel 11, add to bootstrap/providers.php
    return [
        ...
        Spatie\Html\HtmlServiceProvider::class,
    ];
  10. Build forms coupled to a model

    main

    For easier model-driven forms, use modelForm() to open a form that is automatically coupled to a model, and closeModelForm() to close it. This is a convenience wrapper around model() and endModel().

    {{ html()->modelForm($user, 'PUT', '/update-url')->open() }}
    
        {{ html()->text('name') }}
        {{ html()->email('email')}}
    
    {{ html()->closeModelForm() }}
    {{ html()->modelForm($user, 'PUT', '/update-url')->open() }}
    
        {{ html()->text('name') }}
        {{ html()->email('email')}}
    
    {{ html()->closeModelForm() }}
  11. Upgrade from v1 to v2

    main

    Upgrading from version 1 to version 2 is generally non-breaking via composer update, but be aware of the following:

    • html() helper: The package now provides a global html() function by default. If you have a custom html() method defined in your application, you must remove it to avoid conflicts.
    • Typehints: Various typehints were removed to improve flexibility (e.g., allowing arrays for multiple select elements). If you have extended any classes from the package, you will need to update your method signatures to match the new, less restrictive typehints.
  12. Install spatie/laravel-html

    main

    Install the package using Composer:

    composer require spatie/laravel-html

    To use the Html facade with a shorter name, optionally register an alias in your config/app.php file:

    // config/app.php
    'aliases' => [
        ...
        'Html' => Spatie\Html\Facades\Html::class,
    ];