putyourlightson / laravel-datastar
A reactive hypermedia framework for Laravel.
Fund package maintenance!
bencroker
Requires
- php: ^8.2
- laravel/framework: >=11.0
- starfederation/datastar-php: ^1.0.0-RC.3
Requires (Dev)
- craftcms/ecs: dev-main
- craftcms/phpstan: dev-main
- pestphp/pest: ^3.0
This package is auto-updated.
Last update: 2025-08-10 07:51:32 UTC
README
Datastar Package for Laravel
A reactive hypermedia framework for Laravel.
Warning
Updating from a previous version? View the release notes.
This package integrates the Datastar hypermedia framework with Laravel, allowing you to create reactive frontends driven by Blade views or controllers. It aims to replace the need for front-end frameworks such as React, Vue.js and Alpine.js + htmx, and instead lets you manage state and use logic from your Laravel backend.
Use-cases:
- Live search and filtering
- Loading more elements / Infinite scroll
- Paginating, ordering and filtering lists
- Submitting forms and running actions
- Pretty much anything to do with reactive front-ends
License
This package is licensed for free under the MIT License.
Requirements
This package requires Laravel 11.0.0 or later.
Installation
Install manually using composer, then run the artisan vendor:publish --tag=public
command to publish the public assets.
composer require putyourlightson/laravel-datastar:^1.0.0-RC.3 php artisan vendor:publish --tag=public
Overview
The Datastar package for Laravel allows you to handle backend requests by sending SSE events using Blade directives in views or using controllers. The former requires less setup and is more straightforward, while the latter provides more flexibility.
Here’s a trivial example that toggles some backend state using the Blade view datastar/toggle.blade.php
to handle the request.
{{-- main.blade.php --}} <div data-signals-enabled="false"> <div data-text="$enabled ? 'ON' : 'OFF'"></div> <button data-on-click="{{ datastar()->view('datastar.toggle') }}"> <span id="button-text">Enable</span> </button> </div>
{{-- datastar/toggle.blade.php --}} @php $enabled = $signals['enabled'] ?? false; @endphp @patchsignals(['enabled' => $enabled]) @patchelements <span id="button-text"> {{ $enabled ? 'Disable' : 'Enable' }} </span> @endpatchelements
Usage
Start by reading the Getting Started guide to learn how to use Datastar on the frontend. The Datastar package for Laravel only handles backend requests.
Note
The Datastar VSCode extension and IntelliJ plugin have autocomplete for all data-*
attributes.
When working with signals, note that you can convert a PHP array into a JSON object using the json_encode
function.
{{-- main.blade.php --}} @php $signals = ['foo' => 1, 'bar' => 2]; @endphp <div data-signals="{{ json_encode($signals) }}"></div>
Datastar Helper
The datastar()
helper function is available in Blade views and returns a Datastar
helper that can be used to generate action requests to the Datastar controller. The Datastar controller can either render a view, run a controller action, or call a route, each of which respond by sending an event stream containing zero or more SSE events.
Signals are also sent as part of the request, and are made available in Datastar views using the $signals
variable.
datastar()->view()
Returns a @get()
action request to render a view. The value should be a dot-separated path to a Blade view.
// Sends a `GET` request that renders a Blade view {{ datastar()->view('path.to.view') }}
Variables can be passed in as a second argument, that will be available in the rendered view.
{warning} Variables are tamper-proof yet visible in the source code in plain text, so you should avoid passing in any sensitive data. Only primitive data types can be used as variables: strings, numbers, booleans and arrays. Objects and models cannot be used.
// Sends a `GET` request that renders a Blade view {{ datastar()->view('path.to.view', ['foo' => 'bar']) }}
Options can be passed into the @get()
action using a third argument.
// Sends a `GET` request that renders a Blade view {{ datastar()->view('path.to.view', ['foo' => 'bar'], ['contentType' => 'form']) }}
datastar()->action()
Returns a @post()
action request to run a controller action. The value should be an array with a controller class name as the first value and an action name as the second. A CSRF token is automatically generated and sent along with the request.
// Sends a `POST` request that runs a controller action {{ datastar()->action(['MyController', 'update']) }}
Params can be passed in as a second argument, that will be available as arguments to the controller action
{warning} Params are tamper-proof yet visible in the source code in plain text, so you should avoid passing in any sensitive data. Only primitive data types can be used as params: strings, numbers, booleans and arrays. Objects and models cannot be used. Route-model binding works with controller actions.
// Sends a `POST` request that runs a controller action {{ datastar()->action(['MyController', 'update'], ['foo' => 'bar']) }}
Options can be passed into the @get()
action using a third argument.
// Sends a `POST` request that runs a controller action {{ datastar()->action(['MyController', 'update'], ['foo' => 'bar'], ['contentType' => 'form']) }}
datastar()->get()
Returns a @get()
action request that calls a route. The value must be a defined route.
// Sends a `GET` request to a route {{ datastar()->get('/uri') }}
Options can be passed into the @get()
action using a second argument.
// Sends a `GET` request to a route {{ datastar()->get('/uri', ['contentType' => 'form']) }}
datastar()->post()
Works the same as datastar()->get()
but returns a @post()
action request that calls a route. A CSRF token is automatically generated and sent along with the request.
// Sends a `POST` request to a route {{ datastar()->post('/uri') }}
datastar()->put()
Works the same as datastar()->post()
but returns a @put()
action request that calls a route.
// Sends a `PUT` request to a route {{ datastar()->put('/uri') }}
datastar()->patch()
Works the same as datastar()->post()
but returns a @patch()
action request that calls a route.
// Sends a `PATCH` request to a route {{ datastar()->patch('/uri') }}
datastar()->delete()
Works the same as datastar()->post()
but returns a @delete()
action request that calls a route.
// Sends a `DELETE` request to a route {{ datastar()->delete('/uri') }}
Blade Directives
Datastar Blade directives can patch and remove elements, patch signals, execute JavaScript and redirect the browser.
Patch Elements
The @patchelements
directive allows you to patch elements into the DOM.
{{-- main.blade.php -}} <div id="results"></div> <div id="search"> <button data-on-click="{{ datastar()->get('datastar.search') }}"> Search </button> </div>
{{-- datastar/search.blade.php -}} @patchelements <div id="results"> ... </div> @endpatchelements @patchelements <div id="search"> Search complete! </div> @endpatchelements
This will swap the elements with the IDs results
and search
into the DOM. Note that elements with those IDs must already exist in the DOM, unless a mode is specified (see below).
Element Patch Options
Elements are patched into the DOM based on element IDs, by default. It’s possible to pass other modes and other element patch options in as an argument.
@patchelements(['selector' => '#list', 'mode' => 'append']) <li>A new list item</li> @endpatchelements
Automatic Element Patching
Any elements output in a Datastar template (outside any @patchelements
tags) will be automatically wrapped in a @patchelements
directive. This makes it possible to write your views in a way that makes them more reusable.
{{-- datastar/search.blade.php -}} <div id="results"></div>
The view above is the equivalent of writing:
{{-- datastar/search.blade.php -}} @patchelements <div id="results"></div> @endpatchelements
While automatic element patching is convenient, it is less explicit and more restrictive (since element patch options cannot be used), so should only be used when appropriate.
Remove Elements
Elements can be removed from the DOM using the @removeelements
directive, which accepts a CSS selector.
@removeelements('#list')
Patch Signals
The @patchsignals
directive allows you to patch signals into the frontend signals.
{{- Sets the value of the `username` signal. -}} @patchsignals(['username' => 'johnny']) {{- Sets multiple signal values using an array of key-value pairs. -}} @patchsignals(['username' => 'bobby', 'success' => true]) {{- Removes the `username` signal by setting it to `null`. -}} @patchsignals(['username' => null])
{note} Signals patches cannot be wrapped in
@patchelements
directives, since each patch creates a server-sent event which will conflict with the element’s contents.
Signal Patch Options
It’s possible to pass signal patch options in as a second argument.
@patchsignals(['username' => 'johnny'], ['onlyIfMissing' => true])
Executing JavaScript
The @executescript
directive allows you to send JavaScript to the browser to be executed on the front-end.
@executescript alert('Username is valid'); @endexecutescript
Execute Script Options
It’s possible to pass execute script options in as an argument. They are applied to the <script>
tag that is appended to the DOM.
@executescript(['autoRemove' => true, 'attributes' => ['defer' => true]]) alert('Username is valid'); @endexecutescript
Redirecting
The @location
directive allows you to redirect the page by updating window.location
on the front-end.
@location('/guide')
Location Options
It’s possible to pass location options in as a second argument. They are applied to the <script>
tag that is appended to the DOM.
@location('/guide', ['autoRemove' => true, 'attributes' => ['defer' => true]])
Using Controllers
You can send SSE events from your own controller using the sse()
helper. No routes are required, as Datastar will handle routing to the controller action you specify when using the Datastar helper.
{{-- main.blade.php --}} // Sends a `POST` request that runs a controller action {{ datastar()->action(['MyController', 'update']) }}
namespace App\Http\Controllers; use Illuminate\Routing\Controller; use Symfony\Component\HttpFoundation\StreamedResponse; class MyController extends Controller { public function index(): StreamedResponse { $signals = sse()->readSignals(); sse()->patchSignals(['enabled' => $signals['enabled'] ? false : true]); sse()->patchElements(' <span id="button-text">' . ($signals['enabled'] ? 'Enable' : 'Disable') . '</span> '); return sse()->getEventStream(); } public function view(): StreamedResponse { sse()->renderView('path.to.view'); return sse()->getEventStream(); } }
Controller actions must return a StreamedResponse
created using the getEventStream()
method.
sse()
Helper
The sse()
helper provides methods to patch elements, patch signals, execute scripts, redirect the browser and render Datastar views.
patchElements()
Patches elements into the DOM. Accepts element patch options as an optional second argument.
sse()->patchElements('<div id="new-element">New element</div>');
removeElements()
Removes elements that match the provided selector from the DOM.
sse()->removeElements('#list');
patchSignals()
Patches signals into the frontend. Accepts signal patch options as an optional second argument.
sse()->patchSignals(['foo' => 1, 'bar' => 2]);
executeScript()
Executes JavaScript in the browser. Accepts execute script options as an optional second argument, which are applied to the <script>
tag that is appended to the DOM.
sse()->executeScript('alert("Hello, world!")');
location()
Redirects the browser by setting the location to the provided URI. Accepts location options as an optional second argument, which are applied to the <script>
tag that is appended to the DOM.
sse()->location('/guide');
renderView()
Renders a Datastar view. Accepts the view path as the first argument and an optional array of variables as the second argument. The Blade view should output Datastar directives.
sse()->renderView('datastar.toggle', ['enabled' => true]);
Signals
Signals can be accessed within views rendered by Datastar using the signals variable, which is an array of signals received by the request that is automatically injected into the template.
<input data-bind-username> <button data-on-click="{{ datastar()->get('path.to.view') }}"> Check </button>
@php $username = $signals['username']; @endphp
If you ever need to read the signals in a request that is not handled by the Datastar package, you can do so as follows.
@php $signals = sse()->readSignals(); @endphp
Note
Signal patches cannot be wrapped in @patchelements
directives, since each update creates a server-sent event which will conflict with the element’s contents.
Created by PutYourLightsOn.