Skip to content
Laravel package

Laravel Enumify

Generate TypeScript enums from your Laravel PHP enums with one Artisan command, labels and methods included, and refactor hardcoded values into enum references.

composer require devwizardhq/laravel-enumify
View on GitHub
On this page

Overview

Laravel Enumify turns the PHP enums in your Laravel app into TypeScript files, so the frontend and backend share one source of truth for statuses, roles and types. Each enum becomes a const object plus a matching type, with a Utils object holding its labels and custom methods.

Generation is one Artisan command, enumify:sync, and the companion Vite plugin Enumify runs it for you during dev and build. The package also ships enumify:refactor, which finds hardcoded enum values in your code and rewrites them to enum references. It is MIT licensed and maintained by the DevWizard core team.

Features

  • One TypeScript file per enum, with a barrel index.ts you can switch off.
  • Cases exported as an as const object, plus a type with the same name.
  • label() or a static labels() method becomes a label helper.
  • Public, non-static, zero-argument methods that return string, int, float, bool or a nullable or union of those become helpers too. Boolean methods become predicates.
  • A generated options() helper that lists every case.
  • File names in kebab, camel or pascal case.
  • Include and exclude filters that accept class names or glob patterns.
  • Optional localization mode for React or Vue, generating a use...Utils hook instead of a static object.
  • Manifest file with hashes so unchanged files are skipped, plus --dry-run, --force and JSON output.
  • enumify:refactor for replacing hardcoded values and normalizing case names to UPPERCASE.
  • A HasLabels contract and an EnumHelpers trait for the PHP side.

Requirements

  • PHP 8.2 or newer.
  • Laravel 10, 11, 12 or 13 (illuminate/support and illuminate/contracts).
  • For automatic syncing, the npm package Vite plugin Enumify, which supports Vite 4 to 8 and Node 18 or newer.

Installation

Terminal
composer require devwizardhq/laravel-enumify

Then scaffold the output folder:

Terminal
php artisan enumify:install

The installer creates the output directory and a .gitkeep file, prints the .gitignore lines to add, can append them, and offers to publish the config. It also offers to install the Vite plugin with the package manager it detects from your lock file. To do that step yourself:

Terminal
pnpm add -D @devwizard/vite-plugin-enumify

Configuration

Publish the config file:

Terminal
php artisan vendor:publish --tag="enumify-config"

The keys in config/enumify.php:

Key Default Description
paths.enums ['app/Enums'] Directories scanned for enums
paths.models ['app/Models'] Directories scanned for enum casts by enumify:refactor
paths.output 'resources/js/enums' Where TypeScript files are written
naming.file_case 'kebab' File name case: kebab, camel or pascal
features.generate_union_types true Export a type next to each const object
features.generate_label_maps true Generate the label helper
features.generate_method_maps true Generate helpers for custom methods
features.generate_index_barrel true Write an index.ts barrel
localization.mode 'none' none, react or vue
runtime.watch true Read by the Vite plugin to decide whether to watch
filters.include [] Only these enums, by class name or glob; empty means all
filters.exclude [] Skip these enums
refactor.exclude vendor, node_modules, storage and similar Paths enumify:refactor never scans

Usage

Write a normal PHP enum:

PHP
namespace App\Enums;

enum OrderStatus: string
{
    case PENDING = 'pending';
    case SHIPPED = 'shipped';

    public function label(): string
    {
        return match ($this) {
            self::PENDING => 'Pending',
            self::SHIPPED => 'Shipped',
        };
    }

    public function color(): string
    {
        return match ($this) {
            self::PENDING => 'yellow',
            self::SHIPPED => 'green',
        };
    }
}

Generate the TypeScript:

Terminal
php artisan enumify:sync

The file resources/js/enums/order-status.ts exports OrderStatus (the const object and the type) and OrderStatusUtils with label, color and options helpers. Import it in the frontend:

TypeScript
import { OrderStatus, OrderStatusUtils } from '@/enums/order-status';

const status: OrderStatus = OrderStatus.PENDING;

OrderStatusUtils.label(status);
OrderStatusUtils.color(status);
OrderStatusUtils.options();

With the Vite plugin in place, the sync runs on every build and again when files in your enum directories change, so you rarely run it by hand.

API Reference

Artisan commands

Command Purpose
enumify:install Create the output folder, .gitkeep, gitignore lines and optional plugin install. Option --force
enumify:sync Generate TypeScript files from the configured enums
enumify:refactor Replace hardcoded enum values and normalize case names

enumify:sync options:

Option Description
--force Rewrite every file even if unchanged
--dry-run Show what would be generated without writing
--only= Generate a single enum by fully qualified class name
--format= table (default) or json

enumify:refactor options:

Option Short Description
--fix -f Apply the changes
--dry-run -d Preview without changing files
--enum= -e Target one enum by short name
--path= -p Limit the scan to a directory
--interactive -i Guided prompts
--json -j JSON output
--backup Back up files before changing them
--include= File patterns to include, repeatable
--exclude= Paths or patterns to skip, repeatable
--report= Write a report as json, csv or md
--detailed Show code context
--normalize-keys Convert case names to UPPERCASE and update references

The refactor command only touches columns that have an enum cast on a model found in paths.models.

PHP helpers

DevWizardHQ\Enumify\Contracts\HasLabels declares label(): string.

DevWizardHQ\Enumify\Concerns\EnumHelpers is a trait for backed enums with these static methods:

Method Returns
options(string $label = 'label') Array of value to label pairs
selectOptions(string $label = 'label') List of value and label objects for selects
values() All values
names() All case names
hasValue(string or int $value) Whether the value exists

When the label method is missing, labels fall back to a humanized case name.

Examples

Fill a select from the enum with the generated helpers:

TSX
import { OrderStatus, OrderStatusUtils } from '@/enums/order-status';

export function StatusSelect() {
  return (
    <select>
      {OrderStatusUtils.options().map((status) => (
        <option key={status} value={status}>
          {OrderStatusUtils.label(status)}
        </option>
      ))}
    </select>
  );
}

Pass options from a controller using the trait:

PHP
use App\Enums\OrderStatus;
use DevWizardHQ\Enumify\Concerns\EnumHelpers;

enum OrderStatus: string
{
    use EnumHelpers;

    case PENDING = 'pending';
    case SHIPPED = 'shipped';
}

OrderStatus::selectOptions();

Translate labels in React. Install Laravel Localizer React, set the mode and sync again:

PHP
'localization' => [
    'mode' => 'react',
],
TSX
import { useOrderStatusUtils } from '@/enums/order-status';

const { label, options } = useOrderStatusUtils();

Preview, then apply, a refactor with backups:

Terminal
php artisan enumify:refactor --dry-run
php artisan enumify:refactor --fix --backup

FAQ & Troubleshooting

No files are generated. Check that your enums live under paths.enums and are not removed by filters.include or filters.exclude. Run php artisan enumify:sync --dry-run to see what is discovered.

Imports fail during the frontend build. Confirm the output folder exists, with enumify:install if needed, and that the Vite plugin is enabled and listed before the Laravel plugin.

A method is missing from the generated file. Only public, non-static methods with no arguments and a scalar return type are converted. Methods without a return type are skipped.

Should generated files be committed? The installer suggests gitignoring them and keeping the .gitkeep. Since the plugin regenerates them on every build, that works for most teams. Commit them if your build environment has no PHP.

What does the refactor command not touch? Columns without an enum cast on a model, and any path in refactor.exclude. Use --dry-run or --backup first.

Why do I get a localization hook instead of an object? localization.mode is react or vue. Set it to none for a static Utils object.

Changelog & Support

Release history is in the CHANGELOG and on the GitHub releases page. Report bugs and request features in GitHub issues. Source: DevWizardHQ/laravel-enumify. Pair it with Vite plugin Enumify. For help with Laravel and Inertia products, the DevWizard team takes project contracts and monthly retainers through the contact page.