PHPackages                             well35/laravel-enum-objects - 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. well35/laravel-enum-objects

ActiveLibrary

well35/laravel-enum-objects
===========================

Generate frontend enum objects from PHP enums, so backend enums are the single source of truth.

v1.1.0(3w ago)648↓75%MITPHPPHP ^8.4CI passing

Since Jul 15Pushed 3w agoCompare

[ Source](https://github.com/Well35/laravel-enum-objects)[ Packagist](https://packagist.org/packages/well35/laravel-enum-objects)[ RSS](/packages/well35-laravel-enum-objects/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (10)Versions (6)Used By (0)

laravel-enum-objects
====================

[](#laravel-enum-objects)

[![Tests](https://github.com/Well35/laravel-enum-objects/actions/workflows/tests.yml/badge.svg)](https://github.com/Well35/laravel-enum-objects/actions/workflows/tests.yml)[![Latest Version on Packagist](https://camo.githubusercontent.com/57dd6d22e3993e6159207f9aa57e1dcf971f7218d900f26236b9b97a5cb66d8b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f77656c6c33352f6c61726176656c2d656e756d2d6f626a656374732e737667)](https://packagist.org/packages/well35/laravel-enum-objects)[![Total Downloads](https://camo.githubusercontent.com/3228e083e996c9e70bf45165c74c35a9e0db87332f8ca60adafd69baddeb07f2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f77656c6c33352f6c61726176656c2d656e756d2d6f626a656374732e737667)](https://packagist.org/packages/well35/laravel-enum-objects)[![License](https://camo.githubusercontent.com/0458e92402f42c63ce10266c94a6008cb61066cd9036bf8d4a250a065bae6f92/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f77656c6c33352f6c61726176656c2d656e756d2d6f626a656374732e737667)](https://github.com/Well35/laravel-enum-objects/blob/main/LICENSE)

Generate frontend enum objects from your PHP enums, so the backend enum is the single source of truth.

Unlike [spatie/laravel-typescript-transformer](https://github.com/spatie/typescript-transformer), which generates *types*, this package generates *data*. Your frontend gets objects it can iterate for option lists, labels, and metadata, and the union types come from that data.

```
enum BrowseSort: string
{
    case Newest = 'newest';
    case Popular = 'popular';

    public function label(): string
    {
        return match ($this) {
            self::Newest => 'Newest',
            self::Popular => 'Most played',
        };
    }
}
```

becomes `resources/js/enums/BrowseSort.ts`:

```
export const BrowseSort = {
    Newest: { name: "Newest", value: "newest", label: "Newest" },
    Popular: { name: "Popular", value: "popular", label: "Most played" },
} as const;

export type BrowseSort = (typeof BrowseSort)[keyof typeof BrowseSort]["value"];
```

so the frontend can do:

```
import { BrowseSort } from '@/enums/BrowseSort';

Object.values(BrowseSort)          // option lists with labels
BrowseSort.Popular.value           // no magic strings
const foo: BrowseSort = 'newest'   // union type 'newest' | 'popular'
const bar: BrowseSort = 'newst'    // Error: tyop caught before runtime
```

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

[](#installation)

```
composer require well35/laravel-enum-objects
```

Usage
-----

[](#usage)

```
php artisan enum-objects:generate
```

Every enum under `App\Enums` (nested namespaces mirror into subdirectories) gets one file in `resources/js/enums`. Commit the generated files and `.enum-objects.json`.

When an enum is renamed or deleted, its old file is pruned on the next run. Pruning is manifest based and non-destructive. Only files the package itself wrote (listed in the manifest) are ever deleted.

### Labels

[](#labels)

If the enum has a `label()` method it is called per case, otherwise the label falls back to `Str::headline()` of the case name (`VeryHigh` → `"Very High"`).

### Extra properties

[](#extra-properties)

The rule: **static values go on the case, computed values go on a method.**

```
use Well35\EnumObjects\Attributes\ComputedProperty;
use Well35\EnumObjects\Attributes\ObjectProperty;

enum ItemRarity: string
{
    #[ObjectProperty('icon', 'circle')]
    case Common = 'common';

    #[ObjectProperty('icon', 'sparkle')]
    case Legendary = 'legendary';

    #[ComputedProperty]                 // exported as `color`
    public function color(): string
    {
        return match ($this) {
            self::Common => '#999',
            self::Legendary => '#f90',
        };
    }

    #[ComputedProperty('sortWeight')]   // key override
    public function weight(): int { /* ... */ }
}
```

`ComputedProperty` methods must be callable without arguments. Keys are emitted exactly as you write them (`ComputedProperty` defaults to the method name).

`ObjectProperty` on the enum class itself adds the property to every case:

```
#[ObjectProperty('group', 'items')]     // all cases get group: "items"
enum ItemRarity: string { ... }
```

On a key collision the most specific declaration wins: case-level `ObjectProperty` beats `ComputedProperty` beats class-level `ObjectProperty` beats the built-ins.

Keys can also be backed-enum cases instead of strings, so you can define your own key enum instead of repeating strings:

```
enum PropKeys: string { case Group = 'group'; }

#[ObjectProperty(PropKeys::Group, 'items')]
```

### Excluding cases or enums

[](#excluding-cases-or-enums)

`#[Excluded]` keeps a case out of the generated object, or if placed on the enum class, skips the enum entirely:

```
use Well35\EnumObjects\Attributes\Excluded;

enum Priority: int
{
    case Low = 1;

    #[Excluded]
    case Internal = 99;   // never sent to the frontend
}
```

Excluded cases drop out of the union type too, so don't exclude anything the API still returns.

### Inertia, Vue, React

[](#inertia-vue-react)

The generated files are plain data, so nothing framework specific to set up.

Laravel serializes backed enums to their value, so an enum sent through an Inertia prop (or any JSON response) arrives already matching the generated type:

```
return Inertia::render('Browse', [
    'sort' => BrowseSort::Popular,   // arrives as 'popular'
]);
```

```
defineProps();   // 'newest' | 'popular'
```

Vue: a select built from the enum:

```

import { BrowseSort } from '@/enums/BrowseSort';

            {{ option.label }}

```

React: extra properties looked up from the value the backend sent:

```
import { ItemRarity } from '@/enums/ItemRarity';

export function RarityBadge({ rarity }: { rarity: ItemRarity }) {
    const meta = Object.values(ItemRarity).find(option => option.value === rarity)!;

    return {meta.label};
}
```

### Keeping frontend and backend in sync

[](#keeping-frontend-and-backend-in-sync)

Add this one test and CI fails whenever an enum changed without regeneration:

```
use Well35\EnumObjects\EnumObjects;

test('enum objects are in sync', fn () => EnumObjects::assertInSync());
```

Or in CI:

```
php artisan enum-objects:generate --check
```

### Dev watcher

[](#dev-watcher)

The package ships a Vite plugin that reruns the generator whenever a PHP enum changes, so the browser reloads with the new objects:

```
// vite.config.js
import enumObjects from './vendor/well35/laravel-enum-objects/vite-plugin.mjs';

export default defineConfig({
    plugins: [laravel({ /* ... */ }), enumObjects()],
});
```

### Configuration

[](#configuration)

```
php artisan vendor:publish --tag=enum-objects-config
```

```
return [
    'paths' => ['App\\Enums' => 'app/Enums'], // namespace => directory
    'output_path' => 'resources/js/enums',    // generated files + manifest go here
    'format' => 'ts',                         // ts | json
    'label_method' => 'label',
    // output keys for the built-in properties
    'name_key' => 'name',
    'value_key' => 'value',
    'label_key' => 'label',
];
```

### Formatters

[](#formatters)

Keep your formatter off the generated files, or it will fight the sync check:

```
# .prettierignore
resources/js/enums

```

Caveats
-------

[](#caveats)

- Pure (unbacked) enums generate with the case name as value and trigger a warning: PHP can't json\_encode a pure enum, so if it's ever sent to or received from the frontend, back it.
- Deleting `.enum-objects.json` orphans the generated files. The package forgets it wrote them, so renamed/removed enums stop being pruned. Regenerating recreates the manifest.

Credits
-------

[](#credits)

The backend-enums-as-single-source-of-truth generator this package grew from was [IronSinew](https://github.com/IronSinew)'s idea and original implementation.

License
-------

[](#license)

MIT

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance94

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Every ~5 days

Total

5

Last Release

26d ago

Major Versions

v0.2.0 → v1.0.02026-07-15

### Community

Maintainers

![](https://www.gravatar.com/avatar/94d13a7fc5deca1afc6a019d7bedf224addc4f8c047deaa6d18ef88e2bda44e6?d=identicon)[Well35](/maintainers/Well35)

---

Top Contributors

[![Well35](https://avatars.githubusercontent.com/u/52145150?v=4)](https://github.com/Well35 "Well35 (13 commits)")

---

Tags

laravelenumtypescriptfrontendcodegen

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/well35-laravel-enum-objects/health.svg)

```
[![Health](https://phpackages.com/badges/well35-laravel-enum-objects/health.svg)](https://phpackages.com/packages/well35-laravel-enum-objects)
```

###  Alternatives

[laravel/sail

Docker files for running a basic Laravel application.

1.9k220.0M1.5k](/packages/laravel-sail)[laravel/ai

The official AI SDK for Laravel.

1.1k6.4M360](/packages/laravel-ai)[spatie/laravel-medialibrary

Associate files with Eloquent models

6.2k47.7M733](/packages/spatie-laravel-medialibrary)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[laravel/boost

Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.

3.6k31.1M880](/packages/laravel-boost)[propaganistas/laravel-disposable-email

Disposable email validator

6093.4M9](/packages/propaganistas-laravel-disposable-email)

PHPackages © 2026

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