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

Transparency log

Fund package maintenance!

emaia

Statistics

Installs: 80

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.56.1 2026-07-18 03:29 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

php artisan hotwire:install

This scaffolds the JS/CSS entry points, adds every npm dependency declared by the catalog to your package.json, wires the @hotwire Vite alias into your vite.config.{ts,mjs,js}, generates the controller loader stub, runs your package manager (auto-detected from the lockfile — bun/pnpm/yarn/npm), and verifies your views match the install config. Components work out of the box — no controller publish step required.

For leaner installs (subset of catalog deps, --core-only), CI automation (--fix --no-interaction), the auto-generated loader stub, drift detection, extending controllers and the full flag reference, see Advanced installation.

Explore the Docs

You can browse the package docs directly in the terminal:

php artisan hotwire:docs
Interactive search, reading a single doc, and listing everything

This opens an interactive search across all controllers and components. Type a name, category, or keyword to filter:

 ┌ Search controllers and components ───────────────────────────────┐
 │ form                                                              │
 ├───────────────────────────────────────────────────────────────────┤
 │   auto-save           [forms]    Automatically saves a form…      │
 │ › auto-submit         [forms]    Submits a form automatically…    │
 │   clean-query-params  [forms]    Strips empty fields from the…    │
 │   optimistic--form    [turbo]    Dispatches optimistic UI…        │
 └───────────────────────────────────────────────────────────────────┘

Read a specific controller or component directly:

php artisan hotwire:docs auto-submit
php artisan hotwire:docs turbo/progress
php artisan hotwire:docs modal --component

List everything with category and description:

php artisan hotwire:docs --list
php artisan hotwire:docs --list --controller
php artisan hotwire:docs --list --component

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.

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 auto-select auto-submit turbo/progress

Top-level controllers

Controller Identifier Category Dependencies Docs
Accordion accordion utility readme
Animated Number animated-number utility readme
Auto Save auto-save forms readme
Auto Resize auto-resize forms readme
Auto Select auto-select forms readme
Auto Submit auto-submit forms readme
Autofocus autofocus forms readme
Back to Top back-to-top utility readme
Carousel carousel utility embla-carousel readme
Char Counter char-counter forms readme
Chart chart utility echarts readme
Checkbox checkbox forms readme
Checkbox Select All checkbox-select-all forms readme
Clean Query Params clean-query-params forms readme
Clear Input clear-input forms readme
Color Scheme color-scheme utility readme
Conditional Fields conditional-fields forms readme
Alert Dialog alert-dialog overlay readme
Copy To Clipboard copy-to-clipboard utility readme
Disclosure disclosure utility readme
Dropdown dropdown overlay @floating-ui/dom readme
Drawer drawer overlay readme
Error Scroll error-scroll forms readme
File Preserve file-preserve forms readme
File Upload file-upload forms @deltablot/dropzone readme
GTM gtm utility readme
Hotkey hotkey utility readme
Hover Card hover-card overlay @floating-ui/dom readme
Input Mask input-mask forms maska readme
Lazy Image lazy-image utility readme
Map map utility leaflet readme
Modal modal overlay readme
Modal Auto Close modal-auto-close overlay readme
Multi Select multi-select forms @floating-ui/dom readme
Money Input money-input forms readme
OEmbed oembed utility readme
Password Visibility password-visibility forms readme
Popover popover overlay @floating-ui/dom readme
Remote Form remote-form forms readme
Reset Files reset-files forms readme
Rich Text rich-text forms @tiptap/core, @tiptap/starter-kit, @tiptap/extension-placeholder, @tiptap/extension-link, @tiptap/extension-underline readme
Rich Text Toolbar rich-text-toolbar forms readme
Scroll Progress scroll-progress utility readme
Sheet sheet overlay readme
Sidebar sidebar utility readme
Slug slug forms readme
Tabs tabs utility readme
Timeago timeago utility date-fns readme
Toast toast feedback @emaia/sonner readme
Toaster toaster feedback @emaia/sonner readme
Toggle toggle forms readme
Toggle Group toggle-group forms readme
Tooltip tooltip utility @floating-ui/dom readme
Unsaved Changes unsaved-changes forms readme

Turbo

Controllers tied to Turbo Drive / Turbo Frames.

Controller Identifier Dependencies Docs
Frame Src turbo--frame-src @hotwired/turbo readme
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

Publish Stimulus Controllers

Publish the controllers you want to use in your app 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 auto-select

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 modal turbo/progress auto-submit

All at once:

php artisan hotwire:controllers --all

List available controllers (with publication status):

php artisan hotwire:controllers --list

Update only controllers that are already published but differ from the package source:

php artisan hotwire:controllers --outdated

# Non-interactive — useful after composer update in CI or deploy scripts
php artisan hotwire:controllers --outdated --force

--outdated never installs controllers that haven't been published yet, and skips those that are already up to date.

Overwrite existing files:

php artisan hotwire:controllers auto-select --force

Top-level controllers are copied flat to resources/js/controllers/ (e.g. modalresources/js/controllers/modal_controller.js, identifier modal). 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-lazy-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.

Name collisions: package controller names are effectively reserved in resources/js/controllers/. If you write your own controller whose file name matches a package one (e.g. your own tabs_controller.js), the tooling treats it as an outdated copy of the package controller — hotwire:controllers --force (or --outdated --force) and hotwire:check --fix will overwrite it without prompting. Before naming a new controller, check the taken names with php artisan hotwire:controllers --list and pick a different one.

Stimulus Attribute Helpers

Build Stimulus data-* attributes from Blade without hand-writing the verbose markup. The primary stimulus() entry point returns a fluent, chainable builder that is Htmlable (renders directly in {{ }}) and Arrayable (merges into a component's attribute bag):

<div {{ stimulus()
        ->controller('chart', ['name' => 'Likes', 'data' => [1, 2, 3, 4]])
        ->action('chart', 'refresh', 'click')
        ->target('chart', 'canvas') }}>
stimulus();
stimulus_controller($name, $values = [], $classes = [], $outlets = []);
stimulus_action($controller, $method, $event = null, $params = []);
stimulus_target($controller, $target);

stimulus_controller() is an alias for stimulus()->controller(...); stimulus_action() and stimulus_target() are shortcuts for stimulus()->action(...) and stimulus()->target(...).

See Stimulus attribute helpers for values/classes/outlets, action params, stacking multiple controllers, attribute-bag merging and the escaping rules.

Blade Components

Component Blade Category Stimulus Identifier(s) Docs
Form <hw:form> forms auto-submit, unsaved-changes, error-scroll, clean-query-params readme
Field <hw:field> forms readme
Field Group <hw:field.group> forms readme
Field Label <hw:field.label> forms readme
Field Error <hw:field.error> forms readme
Checkbox <hw:checkbox> forms checkbox, auto-submit readme
Checkbox Group <hw:checkbox-group> forms checkbox-select-all, auto-submit readme
Checkbox Group Item <hw:checkbox-group.item> forms checkbox-select-all, auto-submit readme
Color Scheme Script <hw:color-scheme.script> utility readme
Color Scheme Toggle <hw:color-scheme.toggle> utility color-scheme, optional tooltip readme
Conditional Field <hw:conditional-field> forms conditional-fields readme
File <hw:file> forms file-preserve, reset-files readme
File Upload <hw:file-upload> forms file-upload readme
Input <hw:input> forms auto-select, clear-input, input-mask, auto-submit readme
Input Group <hw:input-group> forms readme
Multi Select <hw:multi-select> forms multi-select, clear-input readme
Radio Group <hw:radio-group> forms auto-submit readme
Radio Group Item <hw:radio-group.item> forms auto-submit readme
Rich Text <hw:rich-text> forms rich-text, rich-text-toolbar readme
Select <hw:select> forms auto-submit readme
Switch <hw:switch> forms auto-submit readme
Textarea <hw:textarea> forms auto-resize, char-counter, auto-submit readme
Toggle <hw:toggle> forms toggle, auto-submit readme
Toggle Group <hw:toggle-group> forms toggle-group, toggle, auto-submit readme
Toggle Group Item <hw:toggle-group.item> forms toggle-group, toggle, auto-submit readme
Alert <hw:alert> feedback readme
Flash Container <hw:flash-container> feedback toaster readme
Flash Message <hw:flash-message> feedback toast readme
Skeleton <hw:skeleton> feedback readme
Spinner <hw:spinner> feedback readme
Sticky <hw:sticky> layout readme
Accordion <hw:accordion> display accordion readme
Aspect Ratio <hw:aspect-ratio> display readme
Avatar <hw:avatar> display readme
Badge <hw:badge> display readme
Breadcrumb <hw:breadcrumb> display readme
Button Group <hw:button-group> display readme
Card <hw:card> display readme
Empty State <hw:empty-state> display readme
Item <hw:item> display readme
Kbd <hw:kbd> display readme
Marker <hw:marker> display readme
Pagination <hw:pagination> display readme
Progress <hw:progress> display readme
Separator <hw:separator> display readme
Table <hw:table> display readme
Tabs <hw:tabs> display tabs readme
Navbar <hw:navbar> navigation readme
Navbar Item <hw:navbar.item> navigation readme
Alert Dialog <hw:alert-dialog> overlay alert-dialog readme
Drawer <hw:drawer> overlay drawer readme
Dropdown <hw:dropdown> overlay dropdown readme
Hover Card <hw:hover-card> overlay hover-card readme
Modal <hw:modal> overlay modal readme
Popover <hw:popover> overlay popover readme
Sheet <hw:sheet> overlay sheet readme
Frame <hw:frame> turbo turbo--polling, turbo--view-transition readme
Frame Or Page <hw:frame-or-page> turbo readme
Optimistic <hw:optimistic> turbo readme
Button <hw:button> utility hotkey, tooltip readme
Carousel <hw:carousel> utility carousel readme
Chart <hw:chart> utility chart readme
Icon <hw:icon> utility readme
Map <hw:map> utility map readme
Scroll Progress <hw:scroll-progress> utility scroll-progress readme
Sidebar <hw:sidebar> utility sidebar readme
Timeago <hw:timeago> utility timeago readme

Verify Your Setup

List components and their required controllers:

php artisan hotwire:components

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

Check controllers used in your views (components and direct usage):

php artisan hotwire:check

Scans resources/views for Hotwire components and direct Stimulus controller usagedata-controller attributes and the stimulus_controller() / stimulus()->controller() / ->controllers() / stimulus_action() / stimulus_target() helpers — then verifies two things:

  1. Stimulus controllers — every controller required by a used component, or referenced directly, is published and up to date.
  2. npm dependencies — every external package imported by those controllers (e.g. @emaia/sonner, @floating-ui/dom) 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 (hw by default) and the short <hw:*> form are recognized, so views like <hw:flash-message /> and <x-hw::flash-message /> are detected equally. Only controllers shipped by the package are checked — your own controllers are ignored — and Blade comments and <script>/<style> blocks are stripped first, so commented-out code is skipped.

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

# Also run the detected package manager install command after adding deps
php artisan hotwire:check --fix --install

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

Example output:

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

Required npm dependencies:
  ✓  @emaia/sonner ^2.1.0  (used by toaster, toast)
  ✗  @floating-ui/dom ^1.8.0  missing from package.json (used by dropdown, tooltip)

In interactive mode, hotwire:check asks whether to run the detected package manager install command after adding dependencies. In non-interactive scripts, use --fix --install to run it automatically.

Configuration

// config/hotwire.php

return [
    'prefix' => 'hw', // <hw:modal>
];

Change prefix to use a different prefix for Blade components. E.g. 'prefix' => 'ui'<ui:modal> or <x-ui::modal>.

PhpStorm / Laravel Idea

The package ships ide.json metadata for Laravel Idea, so PhpStorm can complete and navigate <hw:*> components.

For Stimulus helper completions, generate an app-level ide.json:

php artisan hotwire:ide-json

hotwire:install runs this automatically for JS installs. The generated metadata includes package controllers and local controllers from resources/js/controllers, with local controllers taking precedence when they override a package identifier.

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.

Extending the package

Laravel Hotwire uses a single registry as the source of truth for:

  • Blade components
  • Stimulus controllers
  • external npm dependencies
  • docs paths
  • public categories

When adding a new component or controller to this package, update the registry entry in src/Registry/catalog.php.

Example component entry:

'modal' => [
    'class' => \Emaia\LaravelHotwire\Components\Modal::class,
    'view' => 'hotwire::component-views.modal',
    'docs' => 'docs/components/modal.md',
    'category' => 'overlay',
    'controllers' => ['modal'],
],

Example controller entry:

'tooltip' => [
    'source' => 'resources/js/controllers/tooltip_controller.js',
    'docs' => 'docs/controllers/tooltip.md',
    'category' => 'utility',
    'npm' => ['@floating-ui/dom' => '^1.8.0'],
],

More details: docs/registry.md

Testing

composer test

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/libs/turbo.js
import * as Turbo from "@hotwired/turbo";

export default Turbo;

// resources/js/libs/stimulus.js
import {Application} from '@hotwired/stimulus'

const Stimulus = Application.start()
window.Stimulus = Stimulus
export {Stimulus}

// resources/js/controllers/index.js
import {Stimulus} from "../libs/stimulus";
import {registerControllers} from "@emaia/stimulus-lazy-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-lazy-loader

TailwindCSS (v4)

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

@import "tailwindcss";

@import '../../vendor/emaia/laravel-hotwire/resources/css/presets/nova.css';

@source '../../vendor/emaia/laravel-hotwire/resources/css/**/*.css';

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.