PHPackages                             pixelsprout/laravel-chorus - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. pixelsprout/laravel-chorus

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

pixelsprout/laravel-chorus
==========================

Laravel Chorus is an event-sourcing based sync-engine designed to sync subsets of your database to your users' on device browser storage, to enable a low-latency web application.

041PHP

Since Sep 29Pushed 9mo agoCompare

[ Source](https://github.com/Pixelsprout/chorus)[ Packagist](https://packagist.org/packages/pixelsprout/laravel-chorus)[ RSS](/packages/pixelsprout-laravel-chorus/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Chorus
==============

[](#laravel-chorus)

A Laravel-first sync engine designed to seamlessly sync subsets of your database to your users' devices, enabling low-latency web applications.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require pixelsprout/laravel-chorus
```

Then run the installer to set up Chorus and Laravel Reverb:

```
php artisan chorus:install
```

The installer will:

1. Publish the migrations and config files
2. Publish TypeScript utilities to `resources/js/chorus`
3. Check if Laravel Reverb is installed and offer to install it if not
4. Configure your broadcasting settings

Finally, run the migrations:

```
php artisan migrate
```

Usage
-----

[](#usage)

### 1. Add the Harmonics trait to your models

[](#1-add-the-harmonics-trait-to-your-models)

```
use Pixelsprout\LaravelChorus\Traits\Harmonics;

class User extends Model
{
    use Harmonics;

    // Specify which fields should be synced using a property
    // By default, no fields will be synced unless explicitly defined
    protected $syncFields = [
        'name',
        'email',
    ];

    // Alternatively, you can define a method
    public function syncFields(): array
    {
        return [
            'name',
            'email',
        ];
    }

    // Or override the getSyncFields method for more complex logic
    public function getSyncFields(): array
    {
        // You can include dynamic logic here
        $fields = ['name', 'email'];

        if ($this->is_admin) {
            $fields[] = 'role';
        }

        return $fields;
    }

    // Define a filter to limit which records get synced to the client
    public function syncFilter()
    {
        // Only sync records owned by the current user
        return $this->where('user_id', auth()->id());
    }
}
```

### 2. Start the Chorus and Reverb servers

[](#2-start-the-chorus-and-reverb-servers)

```
php artisan chorus:start --reverb
```

This will start both Chorus and the Laravel Reverb WebSocket server. Changes to models using the Harmonics trait will be automatically broadcast to connected clients.

If you don't want to run Reverb from Chorus, you can run:

```
php artisan chorus:start
```

And then start Reverb separately with:

```
php artisan reverb:start
```

### 3. Listen for changes in your frontend

[](#3-listen-for-changes-in-your-frontend)

Chorus comes with built-in TypeScript utilities for integrating with IndexedDB and listening for changes. When you run `chorus:install`, these utilities are published to your `resources/js/chorus` directory.

#### Option 1: Using the provided hooks (Recommended)

[](#option-1-using-the-provided-hooks-recommended)

First, set up the database:

```
// stores/types.ts
import { ChorusDatabase, createChorusDb } from '@/chorus';

interface User {
    id: number;
    name: string;
    email: string;
    created_at: Date;
}

const types = createChorusDb('ChorusDatabase') as ChorusDatabase & {
    users: Dexie.Table;
};

types.initializeSchema({
    users: '++id,name,email,created_at',
});

export { types };
```

Then use the hook in your components:

```
// pages/dashboard.tsx
import { types } from '@/stores/types';
import { useHarmonics } from '@/chorus/use-harmonics';

interface User {
    id: number;
    name: string;
    email: string;
}

export default function Dashboard() {
    const { data: users, isLoading, error, lastUpdate } = useHarmonics('users', types);

    return (

            {isLoading ? (
                Loading users...
            ) : error ? (
                Error: {error}
            ) : (

                    {lastUpdate && Last synchronized: {lastUpdate.toLocaleTimeString()}}

                        {users?.map((user) => (

                                ID: {user.id} - {user.name} - {user.email}

                        ))}

            )}

    );
}
```

The `useHarmonics` hook:

- Sets up IndexedDB storage via Dexie.js
- Listens for real-time updates via WebSockets
- Fetches initial data from the server
- Stores the latest harmonic ID in localStorage to optimize subsequent fetches
- Returns reactive data and loading states

#### Option 2: Using Laravel Echo directly

[](#option-2-using-laravel-echo-directly)

You can also use Laravel Echo directly for more control:

```
import Echo from 'laravel-echo';
import Reverb from '@laravel/reverb-js';

window.Echo = new Echo({
    broadcaster: 'reverb',
    client: new Reverb('ws://localhost:8080/reverb'),
});

// Listen for changes to a specific table
Echo.channel('chorus.table.users')
    .listen('.harmonic.created', (e) => {
        console.log('User changed:', e);
    });

// Listen for changes to a specific record
Echo.channel('chorus.record.users.1')
    .listen('.harmonic.created', (e) => {
        console.log('User 1 changed:', e);
    });

// Listen for changes relevant to the current user
Echo.private('chorus.user.' + userId)
    .listen('.harmonic.created', (e) => {
        console.log('User-specific change:', e);
    });
```

Configuration
-------------

[](#configuration)

You can publish the configuration file with:

```
php artisan vendor:publish --tag=chorus-config
```

This will create a `config/chorus.php` file where you can customize settings.

How It Works
------------

[](#how-it-works)

1. When a model using the `Harmonics` trait is created, updated, or deleted, an event is fired.
2. The event is stored in the database for persistence and broadcast via Laravel's event system.
3. Laravel Reverb broadcasts these events to connected WebSocket clients.
4. Clients listen for these events and update their local state accordingly.
5. When a new client connects, they can fetch the latest state from the harmonics table.

Filtering Synced Records
------------------------

[](#filtering-synced-records)

You can control which records get synced to clients using the `syncFilter` method:

```
class Message extends Model
{
    use Harmonics;

    protected $syncFields = ['content', 'user_id', 'is_read'];

    // Only sync messages that belong to the authenticated user
    public function syncFilter()
    {
        return $this->where(function($query) {
            $query->where('user_id', auth()->id())
                  ->orWhere('recipient_id', auth()->id());
        });
    }
}
```

The `syncFilter` method should return a query builder instance that filters the records to be synced. This applies to both initial data loading and incremental updates.

For example, if you want to sync only user-specific data, you can filter based on the authenticated user's ID. This ensures that clients only receive data relevant to them, reducing bandwidth usage and improving security.

Contributing
------------

[](#contributing)

Contributions are welcome!

License
-------

[](#license)

The MIT License (MIT).

###  Health Score

17

—

LowBetter than 6% of packages

Maintenance41

Moderate activity, may be stable

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/c63a963b5ee807ac8c441434f47ba8aabb3631c1c355eb71f6ddac8580693b7b?d=identicon)[braedencrankd](/maintainers/braedencrankd)

---

Top Contributors

[![braedenfoster](https://avatars.githubusercontent.com/u/99447151?v=4)](https://github.com/braedenfoster "braedenfoster (48 commits)")

### Embed Badge

![Health badge](/badges/pixelsprout-laravel-chorus/health.svg)

```
[![Health](https://phpackages.com/badges/pixelsprout-laravel-chorus/health.svg)](https://phpackages.com/packages/pixelsprout-laravel-chorus)
```

###  Alternatives

[aeon-php/calendar-holidays

Holidays calendar abstraction layer for Aeon Time management framework

15215.2k3](/packages/aeon-php-calendar-holidays)[robinbressan/regex-parser

AST for PCRE Regex

2414.7k](/packages/robinbressan-regex-parser)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
