Simplified action methods for Datastar HTTP actions with smart event detection. These methods reduce verbosity while maintaining full access to Datastar's capabilities.
- Quick Start
- Smart Event Detection
- CSRF-Protected Actions
- Standard Datastar Actions
- Navigate Action
- Dispatch Action
- Event Modifiers
- Options Reference
- Common Patterns
use Dancycodes\Hyper\Html\Html;
// Before: verbose dataOn() syntax
Html::button()->dataOn('click', '@postx("/save")');
// After: simplified action method with smart event detection
Html::button()->postx('/save');
// Forms automatically use 'submit__prevent'
Html::form()->postx('/submit'); // Uses submit__prevent event
// Inputs automatically use 'input' event
Html::input()->get('/search'); // Uses input eventAction methods automatically select the appropriate event based on element type and attributes:
| Element Type | Default Event | Reasoning |
|---|---|---|
form |
submit__prevent |
Prevents default form submission |
button, a |
click |
Standard click interaction |
input[type=checkbox] |
change |
Fires when checked/unchecked |
input[type=radio] |
change |
Fires when selection changes |
input[type=file] |
change |
Fires when file selected |
input[type=color] |
change |
Fires when color selected |
input (text-like) |
input |
Fires on every keystroke/change |
select |
change |
Fires when option selected |
textarea |
input |
Fires on every keystroke |
| All others | click |
Safe default for interactions |
For <input> elements, the smart detection checks the type attribute:
Change Event Types (state-based interactions):
checkbox- Toggle checked stateradio- Select from radio groupfile- Select file from file systemcolor- Pick color from color picker
Input Event Types (continuous feedback):
text,email,password- Text entrynumber,date,time- Numeric/date entrysearch,url,tel- Specialized text entryrange- Slider movement- All other input types default to
input
// Text input - automatically uses 'input' event
Html::input()->type('text')->get('/autosave');
// Generates: data-on:input="@get('/autosave')"
// Checkbox - automatically uses 'change' event
Html::input()->type('checkbox')->patchx('/toggle-active');
// Generates: data-on:change="@patchx('/toggle-active')"
// Radio button - automatically uses 'change' event
Html::input()->type('radio')->patchx('/select-option');
// Generates: data-on:change="@patchx('/select-option')"
// File input - automatically uses 'change' event
Html::input()->type('file')->postx('/upload');
// Generates: data-on:change="@postx('/upload')"
// Select dropdown - automatically uses 'change' event
Html::select()->get('/filter');
// Generates: data-on:change="@get('/filter')"
// Form - automatically uses 'submit__prevent' event
Html::form()->postx('/submit');
// Generates: data-on:submit__prevent="@postx('/submit')"Explicitly pass an event to override the smart default:
// Override form's default submit__prevent
Html::form()->postx('/save', 'change');
// Override checkbox's default change event
Html::input()->type('checkbox')->patchx('/toggle', 'click');
// Add event modifiers to text input
Html::input()->type('text')->get('/search', 'input__debounce.300ms');
// Use window event modifier
Html::div()->get('/refresh', 'click__window');Laravel Hyper extends Datastar with CSRF-protected variants of mutating actions. Always use these for Laravel applications.
POST request with automatic CSRF token inclusion.
public function postx(string $url, ?string $event = null, array $options = []): staticUsage:
// Simple POST (uses smart event: 'click' for button)
Html::button()->postx('/create');
// POST on form (uses smart event: 'submit__prevent')
Html::form()->postx('/submit');
// Explicit event with modifiers
Html::button()->postx('/save', 'dblclick__once');
// With options
Html::button()->postx('/save', null, [
'headers' => ['X-Custom' => 'value'],
'retry' => 3
]);PUT request with automatic CSRF token inclusion.
public function putx(string $url, ?string $event = null, array $options = []): staticUsage:
// Full resource update
Html::button()->putx('/users/123');
// With explicit event
Html::form()->putx('/update', 'submit__prevent');PATCH request with automatic CSRF token inclusion.
public function patchx(string $url, ?string $event = null, array $options = []): staticUsage:
// Partial resource update
Html::button()->patchx('/users/123');
// Toggle status on checkbox change
Html::input()
->type('checkbox')
->patchx('/toggle-status', 'change');DELETE request with automatic CSRF token inclusion.
public function deletex(string $url, ?string $event = null, array $options = []): staticUsage:
// Simple delete
Html::button()->deletex('/items/123');
// With confirmation (use window modifier for global event)
Html::button()->deletex('/items/123', 'click__window');These are the standard Datastar actions without CSRF protection. Use these only when:
- Interfacing with external APIs
- Working with read-only GET operations
- You've handled CSRF another way
GET request to fetch data or HTML.
public function get(string $url, ?string $event = null, array $options = []): staticUsage:
// Simple GET
Html::button()->get('/fetch-data');
// Search on input with debounce
Html::input()->get('/search', 'input__debounce.300ms');
// Infinite scroll on intersection
Html::div()->get('/load-more', 'intersect__once');
// With options
Html::button()->get('/api/data', null, [
'headers' => ['Accept' => 'application/json']
]);Standard Datastar actions without CSRF protection.
public function post(string $url, ?string $event = null, array $options = []): static
public function put(string $url, ?string $event = null, array $options = []): static
public function patch(string $url, ?string $event = null, array $options = []): static
public function delete(string $url, ?string $event = null, array $options = []): static*x variants (postx, putx, patchx, deletex) for Laravel applications instead.
Client-side navigation with optional targeted updates. Updates browser URL and fetches new content without full page reload.
public function navigate(
string $url,
?string $event = null,
?string $key = null,
array $options = []
): staticParameters:
$url: Target URL to navigate to$event: Event trigger (null = smart default)$key: Optional navigation key for targeted updates$options: Datastar fetch options
Usage:
// Simple navigation
Html::a()
->href('/dashboard')
->navigate('/dashboard');
// Targeted navigation (only updates specific fragment)
Html::a()->navigate('/sidebar', null, 'sidebar-content');
// With explicit event
Html::div()->navigate('/page', 'dblclick');
// With options
Html::a()->navigate('/page', null, 'main', [
'headers' => ['X-Partial' => 'true']
]);
// Navigation with event modifiers
Html::a()->navigate('/content', 'click__prevent', 'main');Dispatches custom JavaScript events with optional detail data. Useful for component communication and decoupled architectures.
public function dispatch(
string $eventName,
?string $event = null,
array $detail = []
): staticParameters:
$eventName: Custom event name to dispatch$event: DOM event that triggers dispatch (null = smart default)$detail: Optional event detail data (sent asevent.detail)
Usage:
// Simple event dispatch
Html::button()->dispatch('modal-open');
// With event detail
Html::button()->dispatch('user-selected', null, [
'userId' => 123,
'username' => 'john'
]);
// Explicit trigger event
Html::div()->dispatch('item-clicked', 'dblclick');
// Complex detail data
Html::button()->dispatch('form-ready', null, [
'formId' => 'user-form',
'fields' => ['name', 'email'],
'timestamp' => time()
]);Event modifiers control event behavior. Use Datastar's double underscore (__) syntax:
| Modifier | Description | Example |
|---|---|---|
prevent |
Calls preventDefault() |
click__prevent |
stop |
Calls stopPropagation() |
click__stop |
once |
Fire only once | click__once |
passive |
Passive event listener | scroll__passive |
capture |
Use capture phase | click__capture |
debounce.{time}ms |
Debounce execution | input__debounce.300ms |
throttle.{time}ms |
Throttle execution | scroll__throttle.250ms |
delay.{time}ms |
Simple delay | click__delay.500ms |
window |
Attach to window | resize__window |
outside |
Trigger when click outside | click__outside |
document |
Attach to document | keydown__document |
viewtransition |
Use View Transition API | click__viewtransition |
// Prevent default form submission
Html::form()->postx('/save', 'submit__prevent');
// Debounced search input
Html::input()->get('/search', 'input__debounce.300ms');
// Fire only once
Html::button()->postx('/init', 'click__once');
// Window resize event
Html::div()->get('/layout', 'resize__window');
// Combine multiple modifiers
Html::button()->postx('/save', 'click__prevent__stop__once');
// Throttle scroll event
Html::div()->get('/load-items', 'scroll__throttle.250ms');All action methods accept an optional $options array for Datastar fetch configuration:
Html::button()->postx('/save', null, [
// Custom headers
'headers' => [
'X-Custom-Header' => 'value',
'Accept' => 'application/json'
],
// Retry configuration
'retry' => 3, // Number of retries
'retryDelay' => 1000, // Delay between retries (ms)
// Credentials handling
'credentials' => 'include', // 'omit', 'same-origin', 'include'
// Filter signals sent to server
'filterSignals' => ['userId', 'formData'],
// Content-Type override
'contentType' => 'application/json',
// Timeout
'timeout' => 5000, // Request timeout (ms)
]);// API endpoint with JSON
Html::button()->postx('/api/save', null, [
'headers' => ['Accept' => 'application/json'],
'contentType' => 'application/json'
]);
// Retry on failure
Html::button()->postx('/unreliable-endpoint', null, [
'retry' => 5,
'retryDelay' => 2000
]);
// Filter sensitive signals
Html::form()->postx('/submit', null, [
'filterSignals' => ['email', 'formData']
]);Html::input()
->type('search')
->name('q')
->placeholder('Search...')
->dataBind('searchQuery')
->get('/search', 'input__debounce.300ms')
->dataIndicator('searching');Html::button()
->type('submit')
->class('btn btn-primary')
->dataIndicator('saving')
->postx('/save')
->dataAttr('disabled', '$saving')
->content(
Html::span()->dataShow('!$saving')->text('Save'),
Html::span()->dataShow('$saving')->text('Saving...')
);Html::div()
->id('scroll-trigger')
->get('/load-more', 'intersect__once')
->dataIndicator('loading')
->content(
Html::div()->dataShow('$loading')->text('Loading...')
);Html::button()
->class('btn btn-danger')
->dataIndicator('deleting')
->deletex('/items/123', 'click__window') // window modifier for global confirmation
->dataAttr('disabled', '$deleting')
->text('Delete');Html::form()
->dataSignals(['status' => 'draft'])
->dataIndicator('submitting')
->content(
Html::input()->type('text')->name('title')->dataBind('title'),
// Save as draft
Html::button()
->type('submit')
->postx('/save?status=draft')
->dataAttr('disabled', '$submitting')
->text('Save Draft'),
// Publish
Html::button()
->type('submit')
->postx('/save?status=published')
->dataAttr('disabled', '$submitting')
->text('Publish')
);// Main navigation link
Html::a()
->href('/dashboard')
->class('nav-link')
->navigate('/dashboard', null, 'main-content', [
'headers' => ['X-Partial' => 'main-content']
])
->text('Dashboard');
// Sidebar navigation (updates sidebar only)
Html::a()
->href('/sidebar/users')
->navigate('/sidebar/users', null, 'sidebar', [
'headers' => ['X-Partial' => 'sidebar']
])
->text('Users');Html::input()
->type('text')
->name('title')
->dataBind('title')
->patchx('/autosave', 'input__debounce.500ms')
->dataIndicator('autoSaving')
->dataAttr('title', "$autoSaving ? 'Saving...' : 'Auto-save enabled'");// Smart detection automatically uses 'change' event
Html::input()
->type('checkbox')
->name('active')
->dataBind('isActive')
->patchx('/toggle-active') // No need to specify 'change'!
->dataIndicator('toggling')
->dataAttr('disabled', '$toggling');// Smart detection automatically uses 'change' event for file inputs
Html::input()
->type('file')
->name('avatar')
->dataBind('avatarFile')
->postx('/upload') // No need to specify 'change'!
->dataIndicator('uploading');Migrating from verbose dataOn() syntax to simplified action methods:
// Before
Html::button()->dataOn('click', '@postx("/save")');
// After
Html::button()->postx('/save');
// Before (with event modifiers)
Html::input()->dataOn('input__debounce.300ms', '@get("/search")');
// After
Html::input()->get('/search', 'input__debounce.300ms');
// Before (with options)
Html::button()->dataOn('click', '@postx("/save", {retry: 3})');
// After
Html::button()->postx('/save', null, ['retry' => 3]);