emaia/laravel-hotwire

The complete Hotwire stack for Laravel — Turbo Drive, Turbo Streams, Stimulus controllers and Blade components out of the box.

Maintainers

Package info

github.com/emaia/laravel-hotwire

pkg:composer/emaia/laravel-hotwire

Fund package maintenance!

emaia

Statistics

Installs: 20

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.8.0 2026-04-22 08:23 UTC

This package is auto-updated.

Last update: 2026-04-25 02:08:21 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Laravel Hotwire

The complete Hotwire stack for Laravel — Turbo Drive, Turbo Streams, Stimulus controllers and Blade components out of the box.

Table of Contents

Requirements

Installation

composer require emaia/laravel-hotwire

Publish the configuration file (optional):

php artisan vendor:publish --tag=hotwire-config

Quick Start

The install command scaffolds the Hotwire setup in your Laravel application — JS entry points, Stimulus loader, Turbo imports and CSS custom variants:

php artisan hotwire:install

This will:

  1. Copy JS and CSS scaffolding to resources/
  2. Add @hotwired/stimulus, @hotwired/turbo and @emaia/stimulus-dynamic-loader to your package.json
  3. Show instructions for the next steps

Only the three core dependencies above are added at install time. Extra npm packages required by specific components (e.g. maska, tippy.js, @emaia/sonner) are published on demand by hotwire:check once you actually use a component that depends on them.

Options:

# Overwrite existing files without prompting
php artisan hotwire:install --force

# Install only JS or CSS scaffolding
php artisan hotwire:install --only=js
php artisan hotwire:install --only=css

If a target file already exists and is identical, it is skipped. If it differs, the command asks for confirmation before overwriting (unless --force is used).

Stimulus Controllers

The components depend on Stimulus controllers that must be published to your project so they can be discovered by the bundler (Vite).

Interactive — select which controllers to publish:

php artisan hotwire:controllers

By name — publish a specific controller:

php artisan hotwire:controllers autoselect

Substrate namespace — publish every controller under a substrate folder (turbo, optimistic, dev):

php artisan hotwire:controllers turbo

Multiple arguments — mix names and substrate namespaces:

php artisan hotwire:controllers dialog turbo/progress auto-submit

All at once:

php artisan hotwire:controllers --all

List available controllers (with publish status):

php artisan hotwire:controllers --list

Overwrite existing files:

php artisan hotwire:controllers autoselect --force

Top-level controllers are copied flat to resources/js/controllers/ (e.g. dialogresources/js/controllers/dialog_controller.js, identifier dialog). Controllers under a substrate folder preserve that folder and use Stimulus' -- separator (e.g. turbo/progressresources/js/controllers/turbo/progress_controller.js, identifier turbo--progress). @emaia/stimulus-dynamic-loader discovers and loads them automatically via import.meta.glob.

If a controller already exists and is identical to the package version, the command reports it as up to date. If it differs, it asks for confirmation before overwriting.

List components and their required controllers:

php artisan hotwire:components

Shows each Blade component, its tag, and the Stimulus controllers it depends on — with publish status for each.

Check controllers for components used in your views:

php artisan hotwire:check

Scans resources/views for Hotwire components, then verifies two things:

  1. Stimulus controllers — every controller required by a used component is published and up to date.
  2. npm dependencies — every external package imported by those controllers (e.g. @emaia/sonner, tippy.js, maska) is declared in your package.json (dependencies or devDependencies).

Exits with code 1 if either has pending items (useful for CI).

Both the configured prefix (hwc by default) and the literal hotwire alias are recognized, so views like <x-hwc::flash-message /> and <x-hotwire::flash-message /> are detected equally.

# Auto-publish missing/outdated controllers AND add missing npm deps to devDependencies
php artisan hotwire:check --fix

# Scan a custom path
php artisan hotwire:check --path=resources/views/app

Example output:

  ✓  toaster  up to date  (used by <x-hwc::flash-container>)
  ✓  toast    up to date  (used by <x-hwc::flash-message>)

Required npm dependencies:
  ✓  @emaia/sonner ^2.1.0  (used by toaster, toast)
  ✗  tippy.js ^6.3.7       missing from package.json (used by tooltip)

When --fix adds packages to devDependencies, run your package manager's install command afterwards (bun install, pnpm install, etc.) to actually fetch them.

View Customization

To customize the HTML/Tailwind of the components:

php artisan vendor:publish --tag=hotwire-views

Views published to resources/views/vendor/hotwire/ will take precedence over the package defaults.

Manual Installation

If you prefer to set things up manually instead of using hotwire:install, follow the steps below.

Project setup (using Vite)

// resources/js/app.js
import "./libs";

// resources/js/libs/index.js
import "./turbo";
import "./stimulus";
import "../controllers";

// resources/js/controllers/index.js
import {Stimulus} from "../libs/stimulus";
import {registerControllers} from "@emaia/stimulus-dynamic-loader";

const controllers = import.meta.glob("./**/*_controller.{js,ts}", {
    eager: false,
});

registerControllers(Stimulus, controllers);

Install the required js dependencies:

bun add @hotwired/stimulus @hotwired/turbo @emaia/stimulus-dynamic-loader

TailwindCSS (v4)

Add these settings to your CSS entrypoint resources/css/app.css:

@source '../../vendor/emaia/laravel-hotwire/resources/views/**/*.blade.php';
@custom-variant turbo-frame (turbo-frame[src] &);
@custom-variant modal ([data-dialog-target="dialog"] &);
@custom-variant aria-busy (form[aria-busy="true"] &);
@custom-variant self-aria-busy (html[aria-busy="true"] &);
@custom-variant turbo-frame-aria-busy (turbo-frame[aria-busy="true"] &);

Configuration

// config/hotwire.php

return [
    'prefix' => 'hwc', // <x-hwc::dialog>
];

Change prefix to use a different prefix for Blade components. E.g. 'prefix' => 'hotwire'<x-hotwire::dialog>.

Turbo

This package includes emaia/laravel-hotwire-turbo as a dependency, providing full Turbo integration for Laravel:

  • Turbo Streams — fluent builder for append, prepend, replace, update, remove, morph, refresh and more
  • Turbo Frames<x-turbo::frame> Blade component with lazy loading support
  • DOM helpersdom_id() and dom_class() for consistent element identification
  • Request detectionwantsTurboStream() and wasFromTurboFrame() macros
  • Blade directives@turboNocache, @turboRefreshMethod('morph'), etc.
  • Testing utilitiesInteractsWithTurbo trait with assertTurboStream() assertions
// Example: return Turbo Streams
return turbo_stream()
    ->append('messages', view('messages.item', compact('message')))
    ->remove('modal');

See the full documentation at emaia/laravel-hotwire-turbo.

Components

Component Blade Stimulus Identifier Docs
Dialog <x-hwc::dialog> dialog readme
Confirm Dialog <x-hwc::confirm-dialog> confirm-dialog readme
Flash Container <x-hwc::flash-container> toaster readme
Flash Message <x-hwc::flash-message> toast readme
Loader <x-hwc::loader> readme
Optimistic <x-hwc::optimistic> readme
Timeago <x-hwc::timeago> timeago readme

Stimulus Controllers (standalone)

Stimulus controllers without an associated Blade component. Used directly via data-controller and data-action.

Controllers live flat at the top level (resources/js/controllers/<name>_controller.{js,ts}). Substrate folders (turbo/, optimistic/, dev/) group controllers tied to a specific technical layer and use Stimulus' -- separator in the identifier.

php artisan hotwire:controllers autoselect auto-submit turbo/progress

Top-level controllers

Controller Identifier Dependencies Docs
Animated Number animated-number readme
Auto Save auto-save readme
Autoresize autoresize readme
Autoselect autoselect readme
Auto Submit auto-submit readme
Char Counter char-counter readme
Checkbox Select All checkbox-select-all readme
Clean Query Params clean-query-params readme
Clear Input clear-input readme
Copy To Clipboard copy-to-clipboard readme
Dialog dialog readme
GTM gtm readme
Hotkey hotkey readme
Input Mask input-mask maska readme
Lazy Image lazy-image readme
OEmbed oembed readme
Remote Form remote-form readme
Reset Files reset-files readme
Timeago timeago date-fns readme
Toast toast @emaia/sonner readme
Toaster toaster @emaia/sonner readme
Tooltip tooltip tippy.js readme
Unsaved Changes unsaved-changes readme

Turbo

Controllers tied to Turbo Drive / Turbo Frames.

Controller Identifier Dependencies Docs
Polling turbo--polling @hotwired/turbo readme
Progress turbo--progress @hotwired/turbo readme
View Transition turbo--view-transition readme

Optimistic

Controller Identifier Dependencies Docs
Dispatch optimistic--dispatch @hotwired/turbo readme
Form optimistic--form @hotwired/turbo readme
Link optimistic--link @hotwired/turbo readme

Dev

Controller Identifier Dependencies Docs
Log dev--log readme

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Contributions are welcome via pull requests.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.