wire-elements/wire-extender
Wire Extender lets you render Livewire components anywhere, even outside your Laravel app. Expose components through a secure API endpoint and mount them on external sites or separate services with the JavaScript client, keeping full interactivity without duplicating code.
Wire Extender lets you render Livewire components on pages that live outside your Laravel app (marketing sites, WordPress, static HTML, a separate SPA domain) by exposing a small embed endpoint and a JS bundle.
composer require wire-elements/wire-extenderphp artisan vendor:publish --tag=wire-extender-configAPP_URL correctly — the generated embed script and asset URLs are derived from it, so a wrong value silently breaks every embed in production.<div wire-extender="counter" :count="5"></div>
<script src="https://your-app.test/wire-extender/wire-extender.js"></script>
First use case: a single interactive island (contact form, pricing toggle, counter) on a site you don't want to rebuild in Laravel. Verify it end-to-end locally before worrying about CORS or auth — those are the two things that will bite you next.
Props from the host page. Pass initial state through attributes on the placeholder element rather than baking values into the component. This keeps the component reusable across multiple host pages and lets non-Laravel authors wire it up without touching PHP.
Multiple islands per page. Each placeholder mounts independently; components keep their own state and round-trips. Good for landing pages with several small widgets. Avoid mounting a single giant component — you lose the benefit and pay the full snapshot payload on first paint.
Protected components. Put auth middleware in the package config so the embed endpoint respects your guard. This is the clean way to ship a "logged-in only" widget that renders on an external dashboard without leaking data — the endpoint checks the session, the JS just fails to mount for guests.
Cross-origin by allowlist. Configure allowed origins explicitly per environment (staging host, production host, local dev port). Do not wildcard this in production; the embed endpoint is a public entrypoint into your app's component tree.
Pair with Livewire conventions. Everything you already do inside Livewire — #[Locked] on IDs, authorize() in mount(), validation rules on public properties — still applies. The external page is untrusted input, treat every prop the same way you'd treat a query string.
Version-locked pairing. Wire Extender's major version tracks Livewire's. v2.x targets Livewire v4; the v2.0.4 release explicitly drops Livewire v3 support. Treat the two packages as a single upgrade unit in composer.json and in your CI matrix.
Livewire v3 users must stay on 1.x. v2.0.4 removes Livewire v3 support from the 2.x line. If you're mid-migration on Livewire v3, pin wire-elements/wire-extender:^1.0 — otherwise the upgrade will resolve and then break at runtime when the component hydration contract no longer matches.
APP_URL and asset hosts. The embed script and Livewire's own assets are served from your Laravel host. Behind a proxy or on a multi-domain setup, set ASSET_URL / trusted proxies deliberately or embedded pages will request the wrong origin and fail with opaque 404s.
Route caching. The embed and API routes are registered by the package. If you route:cache in deploy, re-run it after config changes — stale route caches after toggling the feature on are a common "it works locally" failure.
CORS is the first thing to check on a blank widget. Open devtools, look at the network tab for the embed request. A blocked preflight looks identical to a component that never mounted. Confirm the requesting origin is in your allowlist, including scheme and port.
Sessions and cookies across domains. If the embedded component relies on auth, the browser needs to send cookies cross-site. That means SameSite=None; Secure on the session cookie plus a matching session domain — a config change on the Laravel side, not the package side.
Debugging mounts. Failed hydration produces a mostly silent failure in the browser. Temporarily point the embed at a trivial component with a single public property to isolate whether the problem is the transport (CORS/URLs/routes) or your component's mount().
Extension points worth knowing. The published config controls the route prefixes, middleware stack, and origin allowlist — that middleware array is where you'll add rate limiting or a tenancy resolver for multi-tenant embeds. Keep those changes in config rather than editing published routes.
Payload visibility. Props passed to the placeholder are visible in the page source, and the Livewire snapshot is too. Never pass secrets, and make sure anything sensitive is re-derived server-side in mount() rather than trusted from the attribute.
How can I help you explore Laravel packages today?