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

Symfony Livewire Bundle Laravel Package

roflcopter24/symfony-livewire-bundle

A Symfony bundle that brings Livewire-style reactive components to Symfony apps, enabling dynamic, server-rendered interfaces built primarily in PHP with minimal JavaScript and seamless component updates.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you type a single command, read the bundle's composer.json. The livewire major version it pins determines almost everything downstream: Livewire 2 dispatches updates to one route per component (/livewire/message/{name}), Livewire 3 uses a single /livewire/update POST, and the JS/Alpine story differs between them. A 0-star, 0-dependent bundle like this one is a bridge between two frameworks whose internals both move fast — the pinned version is your contract.

# If it isn't on Packagist (likely), pull it from VCS:
composer config repositories.livewire vcs https://gitlab.com/RoflCopter24/symfony-livewire-bundle
composer require roflcopter24/symfony-livewire-bundle:dev-main

Then:

  1. Register in config/bundles.php:
    RoflCopter24\SymfonyLivewireBundle\SymfonyLivewireBundle::class => ['all' => true],
    
  2. Confirm what actually got wired:
    bin/console debug:container --tag=kernel.event_subscriber | grep -i livewire
    bin/console debug:router | grep -i livewire
    
    If there's no route, there is no Livewire — the update endpoint is the whole product. Register it yourself as a fallback:
    #[Route('/livewire/update', name: 'livewire.update', methods: ['POST'])]
    public function update(Request $request): Response { /* forward to Livewire's handler */ }
    
  3. Point the Blade view finder somewhere writable and create your component directory (usually templates/livewire/ and src/Livewire/).
  4. Add the script tag once in your base Twig layout — either the bundle's route-backed asset or a copy of vendor/livewire/livewire/dist/livewire.js served through your normal asset pipeline.

First use case: a counter. It exercises the full loop — initial render, snapshot, POST, re-render — with nothing else to blame.

namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public int $count = 0;

    public function increment(): void { $this->count++; }

    public function render() { return view('livewire.counter'); }
}
{{-- templates/livewire/counter.blade.php --}}
<div>
    <button wire:click="increment">+</button>
    <span>{{ $count }}</span>
</div>

The initial mount is the part least likely to be standardized. Check for a Twig extension ({{ livewire('counter') }}); if none exists, mount from a controller with Livewire::mount('counter')->html() and pass the HTML into Twig with |raw.

Implementation Patterns

Components as thin presentation shells. Livewire components get resolved from the Illuminate container, not Symfony's. Constructor-injecting a Symfony service usually fails or yields an uninitialized stub. The reliable pattern is to keep components dumb — properties in, actions out — and reach for Symfony services explicitly:

public function search(MyRepository $repo) // method injection is the safer seam
{
    $this->results = $repo->find($this->query);
}

If method injection isn't honored either, bind the service into the bridge container in a compiler pass or a bundle bootstrap, and resolve it via the facade. Treat every constructor dependency as something to verify with a dd() early.

Embedded vs. full-page. Embedded components (mounted inside a Twig template) are the day-to-day workhorse. Full-page components need the Symfony route to render the component's initial HTML server-side and the Livewire route to handle updates — two entry points, one component. Keep full-page components on routes you own so Symfony's firewall and access control still apply.

Symfony Security stays outside. Livewire's authorize() and policies are Laravel constructs and won't see your voters. Put access checks in the Symfony controller that mounts the component, and re-check inside actions with an injected Security:

public function delete(int $id): void
{
    $this->security->denyAccessUnlessGranted('ROLE_ADMIN');
    // ...
}

Validation is a fork in the road. Livewire runs Illuminate's validator, so #[Assert\...] attributes are invisible to $this->validate(). Either write Laravel-style rules ('email' => 'required|email') and accept the duplicate schema, or validate in a Symfony Form/Validator inside the action and map errors back onto public properties. Don't half-do both.

Forms and file uploads. Uploads go through Livewire's temporary-upload endpoint, which writes to a disk configured on the Laravel side, not Symfony's Flysystem bundle. Point it at var/uploads and handle the move yourself in the action.

Testing. Livewire::test() needs the Illuminate app booted; under KernelTestCase it usually isn't. The pragmatic suite is HTTP-level: boot the kernel, render the mount route, scrape the snapshot, POST to /livewire/update with a forged payload, assert on the returned JSON effects.html. Slower, but it tests the seam that actually breaks.

Gotchas and Tips

  • Two containers, one debug toolbar. debug:container shows Symfony's bindings only. When a class "can't be resolved," you are almost certainly looking in the wrong container. Keep a tiny command that dumps the bridge container's bindings for sanity.
  • CSRF will bite you first. Symfony expects _token, Livewire posts _token from its own session guard. The /livewire/update route will 419 until you either exempt it in security.yaml (safe-ish: Livewire has a payload checksum) or bridge the token into the Blade snapshot. Symptom: component renders fine, every action returns 419 in the network tab and nothing on the page.
  • Version lockstep. The PHP package and livewire.js carry matching checksums. A stale asset produces "corrupt component snapshot" errors that look like serialization bugs. Never upgrade one without the other.
  • Blade compiled views don't live in var/cache. Illuminate wants storage/framework/views. Point view.compiled at var/cache/blade, create it, and add it to your deploy cache:clear script — Symfony's clear won't touch it, and stale Blade is a classic "my change didn't deploy."
  • No Twig inside Blade. asset(), path(), encore_entry_link_tags(), and trans don't exist there. Either register Blade directives for them in the bundle bootstrap or hardcode paths and accept the debt.
  • Kill Turbo. If Turbo Drive (or any history-managing SPA shim) is active, Livewire navigation and wire:navigate will double-render or drop listeners. Scope Turbo off for Livewire-bearing pages before you debug anything else.
  • Alpine collisions. Livewire 3 ships Alpine. If you already bundle Alpine via Encore/Stimulus, you'll get two instances and x-data will behave erratically. Load exactly one.
  • Errors are swallowed. Livewire returns JSON with effects and, on failure, an error bag — exceptions surface as a generic toast. Flip the bundle's debug/log config and check the Network tab's response body before touching the PHP.
  • kernel.debug ≠ APP_DEBUG. The two frameworks read different flags. Set both in .env.local or you'll chase ghost caching.
  • Fork it. With zero dependents, there's no upstream pressure to fix anything. Vendor the package (repositories: {type: path}) or fork it the moment you depend on it for production, and pin the commit SHA. Budget a day to read the bundle's bootstrap — it's small, and it's the only real documentation.

Extension points worth knowing: the bundle's compiler pass (where Symfony services get exposed to the Illuminate container), the facade/Livewire static entry point for out-of-Blade rendering, and the update controller — overriding that controller is the cleanest way to add tenancy, locale switching, or a custom auth guard to every component at once.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo