Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Splade Core Laravel Package

protonemedia/laravel-splade-core

Splade Core is the engine behind Laravel Splade, letting you build reactive single-page applications using Blade and Vue 3 without writing Vue components. It provides the low-level rendering, routing, and component internals that power the broader Splade ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

Understand what this package actually is before installing it. protonemedia/laravel-splade-core is not a standalone feature package — it's the engine layer extracted from Splade. It ships the Blade compilation/directive layer, the Vue 3 bridge, and the Composition API composables (useForm, useTable, useToast, useModal, useEventBus) that Splade exposes to <script setup> blocks inside Blade files. In practice you almost never install it directly; you install the umbrella package and it arrives as a transitive dependency.

# Typical path — core comes along for the ride
composer require protonemedia/laravel-splade

# Only if you're deliberately building on the core primitives
composer require protonemedia/laravel-splade-core

Where to look first in the repo:

  1. composer.json — confirms the PHP/Laravel version constraints and whether a JS build step is bundled.
  2. src/ — the service provider and the class that registers Blade directives. This is where @splade, @js, and component aliasing are wired up. Anything you'd extend or override lives here.
  3. resources/js/ (and package.json) — the entry point that exports the composables your <script setup> blocks import.
  4. tests/ — the fastest way to learn the contract for directives and payload shapes, since documentation for the core layer is thin.

First use case — a reactive form in a Blade view. This is the 80% case and the reason anyone reaches for Splade:

{{-- resources/views/users/edit.blade.php --}}
<x-splade-form :default="$user" action="{{ route('users.update', $user) }}" method="put">
    <x-splade-input name="name" label="Name" />
    <x-splade-input name="email" type="email" label="Email" />
    <x-splade-submit />
</x-splade-form>

Or, using the Composition API directly, which is what the core package exists to enable:

<script setup>
import { useForm } from '@protonemedia/laravel-splade-core'

const form = useForm({ name: '', email: '' })

const submit = () => form.put('/users/1')
</script>

<template>
    <input v-model="form.name" />
    <p v-if="form.errors.name">{{ form.errors.name }}</p>
    <button :disabled="form.processing" @click="submit">Save</button>
</template>

You get Laravel validation errors back into form.errors without writing a line of fetch/axios glue. That's the value proposition — start there.

Set expectations early: the repo is archived (last release Jan 2024) and has essentially no dependent ecosystem. Treat a version pin as mandatory:

composer require protonemedia/laravel-splade-core:1.4.* --prefer-dist

Implementation Patterns

Server-driven forms

The dominant pattern is "Laravel owns validation, Vue owns the interaction." Keep your FormRequest exactly as you would in a normal controller — Splade reads the 422 JSON error bag and maps keys onto form fields. Two things make this work reliably:

  • Every input's name must match the validation key exactly, including nested syntax (items.0.name for array fields).
  • On success, return a redirect (or a JSON response Splade can interpret), not a rendered view. The client handles the navigation.
public function update(UpdateUserRequest $request, User $user)
{
    $user->update($request->validated());

    return redirect()
        ->route('users.edit', $user)
        ->with('success', 'User updated.');
}

Flash messages → toasts

Wire flash data once (in your layout or AppServiceProvider) and consume it via the composable rather than hand-rolling a notification component:

<script setup>
import { useToast } from '@protonemedia/laravel-splade-core'

const toast = useToast()
toast.success(@js(session('success')))
</script>

@js() is the sanctioned way to hand PHP values to the client — it JSON-encodes with escaping. Use it instead of string-interpolating into a JS expression.

Server-driven tables

For admin screens, useTable is the highest-leverage composable in the package. Instead of paginating in PHP and re-rendering, sorting/searching/paging round-trips to your controller and Splade swaps the rows:

public function index(Request $request)
{
    return view('users.index', [
        'users' => QueryBuilder::for(User::class)
            ->allowedFilters(['name', 'email'])
            ->allowedSorts(['name', 'created_at'])
            ->paginate(15)
            ->withQueryString(),
    ]);
}

The corresponding <x-splade-table> binds to the paginator automatically. The important habit: keep one source of truth for query state (the query string), so a page refresh or a shared URL reproduces the exact view. Splade's partial-reload behaviour depends on this.

Partial reloads and deferred content

Two patterns worth adopting as defaults:

  • <x-splade-link> for internal navigation — it fetches and swaps only what changed, preserving scroll and component state. Use it on anything list-like or tab-like where a full page load feels heavy.
  • <x-splade-defer> for expensive blocks (dashboards, aggregates, external API calls). The shell renders immediately, the heavy fragment streams in. Wrap the underlying query in a route or __invoke so it's independently addressable and cacheable.

Sharing state between components

For cross-component communication (a sidebar toggling a modal, a table filtering a chart), use the event bus rather than lifting state into a global store:

<script setup>
import { useEventBus } from '@protonemedia/laravel-splade-core'
const emit = useEventBus()
emit('filters-updated', { status: 'active' })
</script>

This keeps Blade partials from silently depending on shared module-level state — the failure mode of that approach is subtle and painful to debug.

Integration tips

  • Keep controllers boring. Splade works best when it's a transport layer over conventional Laravel. Any time you find yourself special-casing a response "because Vue," you're probably fighting the grain.
  • Authorization stays server-side. Gate checks belong in the controller/FormRequest, never in v-if. The Blade-rendered markup is the client contract; if the server sends it, the client can act on it.
  • SSR, if you enable it, is a Node process. Run it as a supervised worker alongside PHP-FPM/Octane and make sure it can reach the same app instance. Budget for this — it's the single biggest operational cost of the stack.

Gotchas and Tips

Maintenance is the headline risk

The repo is archived. There is no upstream fix coming for a Vue minor-version incompatibility, an SSR memory leak, or a change in Laravel's Blade compiler internals. Concretely:

  • Pin both sides of the dependency graph — the Composer constraint and the matching npm/Vite toolchain. A floating vue or vite range will eventually break the build with nobody to file a bug against.
  • Vendor the JS source if you're building on the core composables, so a future npm install can't silently change behaviour under you.
  • Audit your Laravel upgrade path. Splade's directive layer compiles Blade; Laravel major versions have historically changed Blade internals. Test php artisan view:cache explicitly on upgrades — it's the canary.
  • Have an exit plan. If the app is long-lived, the realistic migrations are Livewire (closest conceptual match) or Inertia + Vue (closest architectural match if you're already writing <script setup>).

Composition API in Blade: build-step traps

  • The <script setup> block is not interpreted at runtime — it must pass through the Splade/Vite pipeline. php artisan view:clear alone won't help; you need npm run dev (or a rebuild) after editing a view's script block.
  • In production, a stale vite build is the #1 cause of "my form is dead." Wire the asset build into your deploy before view:cache.
  • Cached views + a changed script block can produce confusingly stale behaviour locally. When in doubt: php artisan optimize:clear && npm run build.

Reactivity pitfalls

  • Don't destructure reactive objects. const { name } = useForm(...) breaks reactivity in templates. Keep the object and use form.name.
  • useForm returns a deep-reactive proxy — mutating nested arrays (form.items.push(...)) works, but reassigning the whole object (form = {...}) does not unless you keep a ref.
  • form.processing and form.errors are read-only in practice. Treat them as derived state; don't assign to them.

Validation error mapping

  • Nested field names are the usual culprit when errors "don't show up." A rule on items.*.name produces keys like items.0.name; your bound name attribute must match byte-for-byte.
  • Custom FormRequest messages() are fine — only the keys matter.
  • If you're returning errors from a non-standard response (a JSON API, a third-party validator), the shape must match Laravel's { errors: { field: [msg] } } envelope. Don't assume Splade will normalize it.

File uploads

  • The method must be spoofed for PUT/PATCH with uploads (_method), and the request must be multipart. The <x-splade-form> component handles this; a hand-rolled useForm + fetch will not. If uploads silently fail, check the request's Content-Type in the network tab first.
  • Large uploads go through your normal PHP upload_max_filesize / post_max_size limits — Splade changes nothing there. A 413 looks like a Vue bug but is a php.ini bug.

Debugging playbook

  1. Network tab first. Splade's requests carry a payload describing which fragments to swap. If the response looks right but the DOM doesn't update, the fragment identifier is mismatched — usually a renamed Blade partial.
  2. Vue DevTools second. Inspect the component tree to confirm the composable's state is what you think it is. Most "it's not reactive" reports are a destructuring bug, visible immediately here.
  3. Check @js output. View-source the rendered page. If PHP data looks mangled, you're interpolating into a JS string somewhere instead of using @js.
  4. SSR-only divergences show up as hydration mismatches — content that changes after mount. Usually it's Date.now(), Math.random(), or auth-dependent branching inside a component. Move that logic server-side or gate it behind onMounted.

Config quirks

  • Publish before you override. Splade's config, views, and JS components are publishable; editing them in vendor/ means the next composer install wipes your work. Confirm the publish tags in the service provider before touching anything.
  • Overriding a Vue component should be done by registering a component of the same name in your own resources/js entry after the package's registration — order matters, and it's not obvious from the docs.
  • Directive name collisions with other Blade-extending packages (Livewire in particular) are real. If both are installed, load order in config/app.php / package discovery determines who wins. Don't run both on the same views.

Extension points worth knowing

  • The Blade directive registrar is the sanctioned hook for adding your own @yourdirective-style helpers that compile to JS.
  • The composables are plain JS modules — you can wrap useForm with your own useAppForm that injects CSRF headers, a base URL, or default toast behaviour. This is the cleanest way to add cross-cutting concerns without forking.
  • If you need to share PHP-derived config with the client, expose it once (a @js(window.__app = ...) block in the layout) rather than sprinkling @js through views.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core