Auto-generate TypeScript enums from Laravel PHP enums. Refactor hardcoded values and normalize enum keys.
Laravel Enumify keeps frontend TypeScript enums in sync with backend PHP enums automatically. It also scans your codebase for hardcoded enum values and can refactor them to use proper enum references. Includes tools for normalizing enum case names to UPPERCASE.
npm run dev and npm run build via the Vite pluginlabel() or static labels() become TS mapsindex.ts for clean imports.gitkeep and strict .gitignore patterns supportedcomposer require devwizardhq/laravel-enumify
php artisan enumify:install
This will:
resources/js/enums/resources/js/enums/.gitkeep.gitignore lines to add (and offer to append them)If the installer successfully installed the plugin, you just need to add it to your vite.config.js:
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import enumify from "@devwizard/vite-plugin-enumify";
export default defineConfig({
plugins: [
enumify(),
laravel({
input: ["resources/js/app.ts"],
refresh: true,
}),
],
});
If the automatic installation skipped or failed, manually install the plugin:
npm install @devwizard/vite-plugin-enumify --save-dev
# or
pnpm add -D @devwizard/vite-plugin-enumify
# or
yarn add -D @devwizard/vite-plugin-enumify
// app/Enums/OrderStatus.php
<?php
namespace App\Enums;
enum OrderStatus: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case SHIPPED = 'shipped';
case DELIVERED = 'delivered';
}
Generated TypeScript:
// resources/js/enums/order-status.ts
export const OrderStatus = {
PENDING: "pending",
PROCESSING: "processing",
SHIPPED: "shipped",
DELIVERED: "delivered",
} as const;
export type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];
export const OrderStatusUtils = {
options(): OrderStatus[] {
return Object.values(OrderStatus);
},
};
enum CampusStatus: string
{
case ACTIVE = 'active';
case SUSPENDED = 'suspended';
case INACTIVE = 'inactive';
public function label(): string
{
return match ($this) {
self::ACTIVE => 'Active',
self::SUSPENDED => 'Suspended',
self::INACTIVE => 'Inactive',
};
}
public function color(): string
{
return match ($this) {
self::ACTIVE => 'green',
self::SUSPENDED => 'red',
self::INACTIVE => 'gray',
};
}
public function isActive(): bool
{
return $this === self::ACTIVE;
}
}
Generated TypeScript:
export const CampusStatus = {
ACTIVE: "active",
SUSPENDED: "suspended",
INACTIVE: "inactive",
} as const;
export type CampusStatus = (typeof CampusStatus)[keyof typeof CampusStatus];
export const CampusStatusUtils = {
label(status: CampusStatus): string {
switch (status) {
case CampusStatus.ACTIVE:
return "Active";
case CampusStatus.SUSPENDED:
return "Suspended";
case CampusStatus.INACTIVE:
return "Inactive";
}
},
color(status: CampusStatus): string {
switch (status) {
case CampusStatus.ACTIVE:
return "green";
case CampusStatus.SUSPENDED:
return "red";
case CampusStatus.INACTIVE:
return "gray";
}
},
isActive(status: CampusStatus): boolean {
return status === CampusStatus.ACTIVE;
},
options(): CampusStatus[] {
return Object.values(CampusStatus);
},
};
import { CampusStatus, CampusStatusUtils } from "@/enums/campus-status";
const status: CampusStatus = CampusStatus.ACTIVE;
// Get label
console.log(CampusStatusUtils.label(status)); // 'Active'
// Get custom method value
const badgeColor = CampusStatusUtils.color(status); // 'green'
// Check state (boolean method)
if (CampusStatusUtils.isActive(status)) {
// Allow access
}
// Get all options (e.g., for a dropdown)
const options = CampusStatusUtils.options();
Enumify supports automatic localization for React and Vue applications using @devwizard/laravel-localizer-react or @devwizard/laravel-localizer-vue.
Prerequisites: Install the appropriate localization package for your framework:
# For React
npm install @devwizard/laravel-localizer-react
# For Vue
npm install @devwizard/laravel-localizer-vue
config/enumify.php:'localization' => [
'mode' => 'react', // 'react' | 'vue' | 'none'
],
import { useLocalizer } from "@devwizard/laravel-localizer-react";
export const CampusStatus = {
ACTIVE: "active",
// ...
} as const;
/**
* CampusStatus enum methods (PHP-style)
*/
export function useCampusStatusUtils() {
const { __ } = useLocalizer();
return {
label(status: CampusStatus): string {
switch (status) {
case CampusStatus.ACTIVE:
return __("Active");
// ...
default:
return status;
}
},
options(): CampusStatus[] {
return Object.values(CampusStatus);
},
};
}
import { CampusStatus, useCampusStatusUtils } from "@/enums/campus-status";
function MyComponent() {
const { label, options } = useCampusStatusUtils();
return (
<select>
{options().map((status) => (
<option value={status}>{label(status)}</option>
))}
</select>
);
}
This ensures your enums are fully localized on the frontend while respecting Reactβs Rules of Hooks.
Note: If you enable localization mode but disable label generation (generate_label_maps set to false), the generator will create hooks/composables without the useLocalizer import, since labels are the only feature that uses localization. In this case, youβll get a hook that only contains custom methods and the options() method.
Enumify will convert methods into TypeScript maps when they meet these rules:
string, int, float, bool, or nullable/union combinations of thoseEnumName + MethodName (pluralized for non-boolean methods)Labels are handled separately using label() or labels().
php artisan enumify:install
# Standard sync
php artisan enumify:sync
# Force regenerate all files
php artisan enumify:sync --force
# Preview changes without writing
php artisan enumify:sync --dry-run
# Sync only one enum
php artisan enumify:sync --only="App\Enums\OrderStatus"
# Output as JSON
php artisan enumify:sync --format=json
# Suppress console output (useful for Vite)
php artisan enumify:sync --quiet
Scan your codebase for hardcoded enum values and refactor them to use proper enum references. Only columns with enum casts in models will be refactored. This command also supports normalizing enum case names to UPPERCASE and updating all references throughout your application.
| Option | Short | Description |
|---|---|---|
--fix |
-f |
Apply refactoring changes to files |
--dry-run |
-d |
Preview changes without modifying files |
--enum= |
-e |
Target a specific enum class by short name (e.g., OrderStatus) |
--path= |
-p |
Limit scan to a specific directory (e.g., app/Models) |
--interactive |
-i |
Run in interactive mode with guided prompts |
--json |
-j |
Output results in JSON format |
--backup |
Β | Create backups before applying changes |
--include= |
Β | File patterns to include (e.g., *.php) |
--exclude= |
Β | Paths or patterns to exclude from scanning |
--report= |
Β | Export report to file (formats: json, csv, md) |
--detailed |
Β | Show detailed output with code context |
--normalize-keys |
Β | Convert enum keys to UPPERCASE and fix all references |
# Scan and display hardcoded enum values
php artisan enumify:refactor
# Preview what changes would be made
php artisan enumify:refactor --dry-run
# Apply fixes with backup
php artisan enumify:refactor --fix --backup
# Target a specific enum
php artisan enumify:refactor --enum=OrderStatus
# Limit scan to a directory
php artisan enumify:refactor --path=app/Services
# Export a markdown report
php artisan enumify:refactor --report=refactor-report.md
Note: The refactor command only processes columns that have an enum cast defined in a model. If no cast is available for a column, it will skip refactoring for that column.
The --normalize-keys flag converts enum case names from any case format to UPPERCASE and updates all references in your codebase:
case Active β case ACTIVEcase pending β case PENDINGcase InProgress β case IN_PROGRESS# Preview key normalization changes
php artisan enumify:refactor --normalize-keys --dry-run
# Apply key normalization with backup
php artisan enumify:refactor --normalize-keys --fix --backup
Run the command interactively with guided prompts:
php artisan enumify:refactor --interactive
This mode allows you to:
Publish the config file:
php artisan vendor:publish --tag="enumify-config"
// config/enumify.php
return [
'paths' => [
'enums' => ['app/Enums'],
'output' => 'resources/js/enums',
],
'naming' => [
'file_case' => 'kebab',
],
'features' => [
'generate_union_types' => true,
'generate_label_maps' => true,
'generate_method_maps' => true,
'generate_index_barrel' => true,
],
'runtime' => [
'watch' => true,
],
'filters' => [
'include' => [],
'exclude' => [],
],
];
resources/js/enums/*.ts β one file per enumresources/js/enums/index.ts β barrel exports (optional)resources/js/enums/.enumify-manifest.json β hashes, timestamps, versionsThe generator uses atomic writes and skips unchanged files for speed.
This repo contains two packages:
packages/laravel-enumify (Composer)packages/vite-plugin-enumify (NPM){
"repositories": [
{
"type": "path",
"url": "./packages/laravel-enumify",
"options": { "symlink": true }
}
]
}
You can install the Vite plugin locally with a workspace or a file path:
pnpm add -D ./packages/vite-plugin-enumify
# or
npm install --save-dev ./packages/vite-plugin-enumify
Recommended workflow for both packages:
mainRelease tip: tag releases after merging to main, then publish to Packagist and NPM.
Suggested pipelines:
composer test and composer test-coveragepnpm run build && pnpm run typecheckphp artisan enumify:install or ensure resources/js/enums/.gitkeep exists.laravel().config/enumify.php paths and include/exclude filters.See CHANGELOG.md
MIT. See LICENSE.md