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

Livewire Chat Kit Laravel Package

kauffinger/livewire-chat-kit

A Livewire chat kit for Laravel with ready-made chat UI components: real-time messaging, conversation lists, and message threads. Drop-in Blade components let you add a complete chat interface to any Livewire app with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish everything first, before you write a line of UI.

composer require kauffinger/livewire-chat-kit
php artisan vendor:publish --provider="Kauffinger\LivewireChatKit\ChatKitServiceProvider"
php artisan migrate

Then set your provider key and run php artisan config:clear. Prism reads credentials from config/prism.php — confirm the published config points at the provider you actually have a key for rather than the default.

Where to look first, in this order:

  1. config/livewire-chat-kit.php (or similarly named) — model bindings, provider/model names, streaming on/off, and route/prefix settings are almost always here.
  2. The published migrations — read the table names (conversations, messages, or prefixed with the package vendor name) before you design anything around them. If they collide with a messages table you already have, rename now.
  3. The published Blade views — this is the real source of truth for what the kit renders. If nothing was published to resources/views/vendor/..., the views live in vendor/kauffinger/livewire-chat-kit/resources/views and you should publish them (--tag=views) rather than fight the vendor copy.
  4. The Livewire component class — usually one Chat/ChatPanel component that owns send(), streaming, and persistence.

First use case: a single-user assistant panel behind auth on an internal page. Don't start with multi-tenant threads; start by proving the stream works end-to-end.

// routes/web.php
Route::middleware('auth')->get('/assistant', AssistantPage::class)->name('assistant');
{{-- resources/views/pages/assistant.blade.php --}}
<x-layout>
    <livewire:chat-kit::chat :conversation="$conversation" />
</x-layout>

Create the conversation in your page component's mount() so the chat is always scoped to the current user — don't let the kit create unattached conversations.

Verify the layout has @fluxAppearance / @fluxScripts (Flux) and that the page is Tailwind-compiled (see Gotchas — this bites almost everyone on first install).

Implementation Patterns

Wrap, don't route directly to the package component. Create your own thin Livewire component or full-page component that composes the kit's component. That gives you one place to inject the system prompt, enforce ownership, and swap the model:

final class AssistantPage extends Component
{
    public Conversation $conversation;

    public function mount(): void
    {
        $this->conversation = Conversation::query()->firstOrCreate(
            ['user_id' => auth()->id(), 'name' => 'default'],
        );

        abort_unless($this->conversation->user_id === auth()->id(), 403);
    }
}

Scope every query by owner. Kits at this maturity level typically resolve a conversation by ID from the request. Add an explicit abort_unless in your wrapper and, if the kit exposes a resolveConversation-style hook or a conversation public property, bind only pre-authorized instances. Treat the component like any other endpoint that accepts an ID.

Prompt/persona belongs in your app, not the vendor config. If the kit reads a system prompt from config, prefer overriding it per-conversation in your wrapper and passing it down. Config-level prompts become a merge conflict on every upgrade.

Streaming UI. The kit streams into a named target (typically $this->stream(to: 'response', content: $stream) inside send()). Keep your Blade side simple: one element with the matching wire:stream target id, a wire:loading indicator, and wire:loading.remove on the send button. Don't add wire:poll — it fights the open stream.

Persist the assistant message after the stream completes, not inside the loop. If you're extending the kit, write the final message row in a finally-ish position so a mid-stream provider error doesn't leave a half-message. Store token counts while you're there; you'll want them.

Prism tools / function calling are where the kit earns its keep. Register tools in your own service and hand them to the kit's Prism call site (or override the generation method). Keep tool classes in app/Ai/Tools/ and authorize inside each tool — the model will happily try to query another user's records.

History truncation is your job. Assume the kit sends the full message history. Add a windowing step (last N turns or a token budget) in your wrapper before dispatch, or a long conversation will quietly blow your context limit and your bill.

Testing: assert on persisted state, not on the stream. Use Prism::fake([...]), call ->call('send') (or the kit's method), then assert the assistant Message row exists and assertSee the rendered text. Streaming assertions are flaky; persistence assertions aren't.

Gotchas and Tips

  • Tailwind v4 won't see vendor classes. If the kit ships Blade views in vendor/, none of its utility classes are generated until you add @source "../../vendor/kauffinger/livewire-chat-kit/resources/views"; to your CSS entry. The symptom is a fully unstyled chat with correct markup. This is the #1 first-install failure.
  • Flux Free vs Pro. The project here runs Flux Free v2. Audit the published views for Pro-only components (flux:editor, flux:chart, flux:calendar, date pickers, command palette) and replace them with Free equivalents before wiring anything else.
  • SSE is buffered in production. Works in Herd, dies behind a proxy. Set proxy_buffering off; and X-Accel-Buffering: no in nginx, and ensure no middleware buffers the response. Also raise max_execution_time — a stream holds the worker for the whole generation.
  • Prism is pre-1.0. Pin the exact Prism version in composer.json and read its changelog before composer update. Breaking changes in Prism surface as silent empty responses, not exceptions.
  • You own this code. 54 stars, zero dependents, single maintainer. Publish the views and config, commit them, and treat upgrades as a manual diff against a fresh publish. Don't build a product on top of an unpinned ^ constraint.
  • Check whether migrations are auto-loaded. If the service provider calls loadMigrationsFrom, vendor:publish may produce nothing and you'll assume it silently failed. Run php artisan migrate:status to confirm the tables actually appeared.
  • Rate limits. Add RateLimiter::for('chat', ...) and call it at the top of send(). The kit almost certainly doesn't do this, and one stuck browser tab retrying is a real invoice.
  • Extension points to prefer: published views, published config, and subclassing the chat component. Avoid editing vendor/ and avoid patching the kit's send() if you can wrap it — those are the two things that make the next update painful.
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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle