name: moonshine-frontend-v4 description: Use when working on MoonShine v4 API mode, JWT authentication, OpenAPI generator, SDUI, Alpine.js events, JavaScript helpers, reactive fields, async UI updates, AsyncCallback, or custom Alpine.js components.
MoonShine v4 -- Frontend (API, SDUI, JS & Alpine.js)
Overview
MoonShine v4 provides a complete frontend interaction layer built on Alpine.js. It supports three modes of operation:
- Standard HTML -- Server-rendered Blade views with Alpine.js reactivity (default).
- API mode -- Add
Accept: application/jsonto get JSON responses from CRUD routes, optionally with JWT authentication. - SDUI (Server-Driven UI) -- Request the component tree as a JSON structure for rendering in custom clients (mobile, SPA).
Alpine.js is bundled and initialized automatically. The global MoonShine JS class provides request helpers, UI toggles, and a callback registry for async response handling.
Key Changes from v3
- OpenAPI Generator (
moonshine/oag) is a new package for auto-generating API specs from resources. - AsyncCallback system with
responseHandler,beforeRequest, andafterResponsehooks is new. - JsonResponse namespace changed to
MoonShine\Crud\JsonResponse(wasMoonShine\Laravel\Http\Responses\MoonShineJsonResponse). - JsEvent enum moved to
MoonShine\Support\Enums\JsEvent. - New JS events:
TABLE_ROW_ADDED,TABLE_EMPTY_ROW_ADDED,TAB_ACTIVE.
1. API Mode
Switch any MoonShine route to return JSON by including Accept: application/json in the request header.
Available CRUD Routes
DELETE /admin/resource/{resourceUri}/crud -- Mass delete (ids[])
GET /admin/resource/{resourceUri}/crud -- Listing
POST /admin/resource/{resourceUri}/crud -- Create
PUT /admin/resource/{resourceUri}/crud/{resourceItem} -- Edit
DELETE /admin/resource/{resourceUri}/crud/{resourceItem} -- Delete
When using MoonShine purely as an API backend, disable session middleware in config/moonshine.php:
'middleware' => [
// remove session-related middleware
],
See references/api-jwt.md for full API setup, JWT configuration, and OpenAPI generation.
2. JWT Authentication
Install the JWT package for token-based API authentication:
composer require moonshine/jwt
php artisan vendor:publish --provider="MoonShine\JWT\Providers\JWTServiceProvider"
Add a base64-encoded secret to .env:
JWT_SECRET=YOUR_BASE64_SECRET_HERE
Configure middleware and auth pipeline in config/moonshine.php:
use MoonShine\JWT\JWTAuthPipe;
use MoonShine\JWT\Http\Middleware\AuthenticateApi;
return [
'middleware' => [
// empty -- no session middleware for API mode
],
'auth' => [
// ...
'middleware' => [
AuthenticateApi::class,
],
'pipelines' => [
JWTAuthPipe::class,
],
],
// ...
];
After successful authentication, use the returned token in subsequent requests:
Authorization: Bearer <token>
3. OpenAPI Generator (NEW in v4)
Generate OpenAPI specifications and documentation directly from MoonShine resources.
composer require moonshine/oag
php artisan vendor:publish --provider="MoonShine\OAG\Providers\OAGServiceProvider"
Generate the specification:
php artisan oag:generate
Specification files are output to the resources directory by default:
resources/oag.yamlresources/oag.json
Documentation is served at /docs.
Config options (config/oag.php):
return [
'title' => 'Docs',
'path' => realpath(resource_path('oag.yaml')),
'route' => 'oag.json',
'view' => 'oag::docs',
];
See references/api-jwt.md for complete OpenAPI Generator setup details.
4. SDUI (Server-Driven UI)
Request any MoonShine page as a JSON component tree by adding the X-MS-Structure: true header. The response describes component types, states, attributes, and nested children -- suitable for mobile apps or custom SPA clients.
GET /admin/dashboard HTTP/1.1
X-MS-Structure: true
Response structure keys:
type-- component type (e.g., "Dashboard", "Card", "Heading")components-- array of child componentsstates-- component state data (title, content, level, etc.)attributes-- HTML attributes (class, id, data-* attributes)
Customization Headers
| Header | Effect |
|---|---|
X-MS-Structure: true |
Return JSON component tree |
X-MS-Without-States: true |
Omit component states from response |
X-MS-Only-Layout: true |
Return only the layout structure |
X-MS-Without-Layout: true |
Return page content without layout wrapper |
See references/sdui.md for full SDUI response structure, JSON examples, and all customization options.
5. Alpine.js Integration
Creating Components
Generate a component scaffold:
php artisan moonshine:component MyComponent
In the Blade view, declare the Alpine.js component:
<div x-data="myComponent"></div>
Register the component logic in a separate JS file (loaded via AssetManager):
document.addEventListener("alpine:init", () => {
Alpine.data("myComponent", () => ({
init() {
// component initialization
},
}))
})
Warning: Alpine.js is already installed and running (
window.Alpine). Do not re-initialize it or include Alpine.js again -- this will cause errors.
6. JS Events System
MoonShine uses custom DOM events dispatched via Alpine.js $dispatch to coordinate UI updates. Every event follows the pattern event_name:component_name.
JsEvent Enum
All events are available via MoonShine\Support\Enums\JsEvent:
| Event Pattern | JsEvent Constant | Purpose |
|---|---|---|
fragment_updated:{name} |
JsEvent::FRAGMENT_UPDATED |
Refresh a Fragment area |
table_updated:{name} |
JsEvent::TABLE_UPDATED |
Refresh an async TableBuilder |
table_reindex:{name} |
JsEvent::TABLE_REINDEX |
Reindex table fields after sort |
table_row_updated:{name}-{row-id} |
JsEvent::TABLE_ROW_UPDATED |
Refresh a single table row |
table_row_added:{name} |
JsEvent::TABLE_ROW_ADDED |
Append new cloned row (NEW) |
table_empty_row_added:{name} |
JsEvent::TABLE_EMPTY_ROW_ADDED |
Append/prepend new row (NEW) |
cards_updated:{name} |
JsEvent::CARDS_UPDATED |
Refresh a CardsBuilder |
form_reset:{name} |
JsEvent::FORM_RESET |
Reset form fields |
form_submit:{name} |
JsEvent::FORM_SUBMIT |
Trigger form submission |
modal_toggled:{name} |
JsEvent::MODAL_TOGGLED |
Open/close a Modal |
off_canvas_toggled:{name} |
JsEvent::OFF_CANVAS_TOGGLED |
Open/close an OffCanvas |
popover_toggled:{name} |
JsEvent::POPOVER_TOGGLED |
Open/close a Popover |
toast:{name} |
JsEvent::TOAST |
Trigger a Toast notification |
show_when_refresh:{name} |
JsEvent::SHOW_WHEN_REFRESH |
Re-evaluate showWhen conditions |
tab_active:{name} |
JsEvent::TAB_ACTIVE |
Activate a tab (NEW) |
Dispatching Events from PHP
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
// Simple event string
AlpineJs::event(JsEvent::TABLE_UPDATED, 'index')
// => "table_updated:index"
// Event with parameters
AlpineJs::event(JsEvent::TABLE_UPDATED, 'index', ['var' => 'foo'])
// => "table_updated:index|var=foo"
Dispatching Events from JavaScript
// Native DOM
document.addEventListener("DOMContentLoaded", () => {
dispatchEvent(new CustomEvent("modal_toggled:my-modal"))
})
// Alpine.js $dispatch
this.$dispatch('modal_toggled:my-modal')
See references/js-events-alpine.md for the complete events reference.
7. Response Events
Return events from a controller or resource method so they fire after the response is processed:
use MoonShine\Crud\JsonResponse;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
return JsonResponse::make()
->events([
AlpineJs::event(JsEvent::TABLE_UPDATED, 'index'),
]);
v4 change: The namespace is now
MoonShine\Crud\JsonResponse(wasMoonShine\Laravel\Http\Responses\MoonShineJsonResponsein v3).
8. Blade Directives
@defineEvent
Declare an event listener in HTML:
<div x-data="myComponent"
@defineEvent('table-updated', 'index', 'asyncRequest')
>
</div>
{{-- Generates: @table-updated-index.window="asyncRequest" --}}
Signature:
@defineEvent(string|JsEvent $event, ?string $name = null, ?string $call = null, array $params = [])
@defineEventWhen
Conditionally declare an event listener:
<div x-data="myComponent"
@defineEventWhen($isAsync, 'table-updated', 'index', 'asyncRequest')
>
</div>
Signature:
@defineEventWhen(mixed $condition, string|JsEvent $event, ?string $name = null, ?string $call = null, array $params = [])
9. AlpineJs Helper
AlpineJs::event()
Build event strings for use in PHP:
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
AlpineJs::event(JsEvent::TABLE_UPDATED, 'index', ['var' => 'foo'])
// => "table_updated:index|var=foo"
AlpineJs::eventBlade()
Build Blade attribute strings for x-on directives:
AlpineJs::eventBlade(JsEvent::FORM_RESET, 'main-form')
// => "@form-reset-main-form.window"
// Usage in customAttributes:
FormBuilder::make('/crud/update')
->name('main-form')
->customAttributes([
AlpineJs::eventBlade(JsEvent::FORM_RESET, 'main-form') => 'formReset',
])
10. Global MoonShine JS Class
The window.MoonShine object is available after the moonshine:init event fires.
MoonShine.request()
MoonShine.request(ctx, '/url', method = 'get', body = {}, headers = {}, data = {})
MoonShine.ui.toast()
MoonShine.ui.toast('Hello world', 'success')
MoonShine.ui.toggleModal()
MoonShine.ui.toggleModal('modal-name')
MoonShine.ui.toggleOffCanvas()
MoonShine.ui.toggleOffCanvas('canvas-name')
MoonShine.iterable.sortable()
MoonShine.iterable.sortable(container, url, group, events, attributes = { handle: '.handle' }, callback)
MoonShine.iterable.reindex()
MoonShine.iterable.reindex(container, itemSelector = '.item')
11. AsyncCallback (NEW in v4)
Components with async behavior (ActionButton, FormBuilder, TableBuilder, fields implementing HasAsyncContract) support AsyncCallback for hooking into the request lifecycle.
PHP Setup
use MoonShine\Support\DTOs\AsyncCallback;
ActionButton::make('Do Something')
->method('myMethod', callback: AsyncCallback::with(
beforeRequest: 'myBeforeRequest',
responseHandler: 'myResponseHandler',
afterResponse: 'myAfterResponse',
))
JS Registration
document.addEventListener("moonshine:init", () => {
// Before request -- receives element and Alpine component
MoonShine.onCallback('myBeforeRequest', function(element, component) {
console.log('Before request', element, component)
})
// Full response handler -- replaces default behavior
MoonShine.onCallback('myResponseHandler', function(response, element, events, component) {
console.log('Custom handler', response)
})
// After successful default processing
MoonShine.onCallback('myAfterResponse', function(data, messageType, component) {
console.log('After response', data, messageType)
})
})
Note: If you specify
responseHandler, it takes full control of response handling andafterResponsewill not be called.
See references/js-events-alpine.md for complete AsyncCallback parameter details.
12. Reactive Fields
MoonShine supports reactive form fields that send their values to the server on change and receive updated field HTML in return. When a reactive field value changes:
- A POST request is sent to
reactiveUrlwith{ _component_name, values }. - The server returns
{ fields: { column: html }, values: { column: value } }. - The field wrapper HTML is replaced and focus is preserved on text inputs.
13. Fragment-Based Partial Updates
Wrap components in a Fragment for partial page updates without full reload:
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\Crud\Components\Fragment;
Fragment::make([
TableBuilder::make()->fields($fields)->items($items)->name('data-table')
])->name('data-fragment'),
ActionButton::make('Reload')
->dispatchEvent(AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'data-fragment'))
Cross-References
- moonshine-security-v4 -- JWT auth configuration, auth pipelines, guard setup, and the
AuthenticateApimiddleware. - moonshine-components-v4 -- Async components (FormBuilder, TableBuilder, Fragment), event wiring, and the full component API.
- moonshine-resources-v4 -- API endpoints for resources, CRUD routes,
JsonResponsemodifiers, and resource method handlers. - moonshine-appearance-v4 -- Asset management for custom JS/CSS files.
Reference Files
references/api-jwt.md-- Full API mode setup, JWT package configuration, and OpenAPI Generator.references/sdui.md-- SDUI concept, response structure, headers, component types, and state management.references/js-events-alpine.md-- JavaScript events reference, Alpine.js integration, AsyncCallback details, and global MoonShine class.