| Install | |
|---|---|
composer require christyoga123/vuelament |
|
| Latest Version: | 1.7.7 |
| PHP: | ^8.2 |
A lightning-fast, lightweight, and modern Admin Dashboard & CRUD Generator for Laravel.
Built on the VILT stack (Vue 3, Inertia.js, Laravel, Tailwind CSS) and powered by Shadcn Vue for a gorgeous UI.
ResourceRouteController handles all CRUD routing automatically — zero boilerplate.Due to frequent issues with automated Shadcn-Vue installations and Tailwind CSS versions, we highly recommend setting up Vuelament and Shadcn-Vue manually following these best practices. Let's build your panel step-by-step!
Gunakan constraint versi stabil (bukan dev-main) agar dapat update yang ter-tag (mis. 1.7.1):
composer require christyoga123/vuelament:^1.7
Atau di composer.json:
"christyoga123/vuelament": "^1.7"
Lalu composer update christyoga123/vuelament akan mengambil rilis terbaru (mis. 1.7.1). Hindari dev-main di production.
Run the install command to publish config & Blade, generate the panel provider, and scaffold Vite/Inertia. Vue/JS assets (Pages, Layouts, components) are not copied — they stay in vendor/christyoga123/vuelament/resources/js, so when you run composer update christyoga123/vuelament the UI (forms, tables, etc.) updates automatically.
php artisan vuelament:install
To override or customize specific Vue files, copy them into your app first:
php artisan vendor:publish --tag=vuelament-views
Then edit the copies in resources/js/Pages/Vuelament, resources/js/Layouts, or resources/js/components/vuelament as needed.
Install Inertia, Vue, Tailwind plugins, and other essential Vuelament dependencies:
npm install @inertiajs/vue3 @vitejs/plugin-vue @vueuse/core @vueup/vue-quill @vuepic/vue-datepicker lucide-vue-next vue-sonner reka-ui class-variance-authority clsx tailwind-merge tw-animate-css
npm install -D typescript vue-tsc
Run the Shadcn-Vue initialization wizard:
npx shadcn-vue@latest init
Choose your preferred settings. Make sure to match this components.json layout, specifically setting "typescript": false and your aliases to @/components and @/lib/utils:
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": false,
"tailwind": {
"config": "",
"css": "resources/css/app.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"composables": "@/composables"
}
}
Vuelament relies on several key components. Install them in one go:
npx shadcn-vue@latest add alert-dialog avatar breadcrumb button card checkbox dialog dropdown-menu input label pagination popover radio-group scroll-area select separator sheet sidebar skeleton sonner switch table textarea tooltip
Tip: If you encounter an error trying to pull the sidebar component (due to strict typescript rules), temporarily change "typescript": true inside components.json, run the add command again, and revert back to false.
Finally, finish the setup by running your migrations, creating an initial user, and starting your dev server:
php artisan migrate
php artisan vuelament:user
npm run dev
php artisan serve
Visit http://localhost:8000/admin/login to access your dashboard!
Upgrading: If you see "Failed to resolve import @vuelament/AppWrapper.vue", your vite.config.js is missing the @vuelament alias — run php artisan vuelament:install once. If you see "Failed to resolve import @/lib/utils", run install to create resources/js/lib/utils.js. If the sidebar overlaps the main content (layout broken), Tailwind isn’t scanning the vendor Vue files — run php artisan vuelament:install to add @source "../../vendor/christyoga123/vuelament/resources/js/**/*.vue" to resources/css/app.css so classes like lg:pl-64 are generated. If you previously published Vue files, remove them from resources/js so the app uses the copy in vendor.
Shadcn-Vue components live in your app (resources/js/components/ui/), so they don’t update automatically. To pull the latest version of a component from the registry, run:
npx shadcn-vue@latest add <component-name>
Example: npx shadcn-vue@latest add button sonner. If the file already exists, the CLI will usually ask whether to overwrite. Choose overwrite to get the new code — if you’ve customized that file, merge your changes manually or keep a backup first.
app/Vuelament/{Panel}/{Model}/
├── Resources/ ← CRUD page classes (like Filament)
│ ├── List{Model}s.php → getHeaderActions(): [CreateAction::make()]
│ ├── Create{Model}.php → getHeaderActions(), getFormActions()
│ └── Edit{Model}.php → getHeaderActions(), getFormActions()
│
├── Pages/ ← Custom pages (non-CRUD)
│
├── Services/ ← Business logic (service-based actions)
│ └── {Model}Service.php
│
├── Widgets/ ← Dashboard widgets
│
└── {Model}Resource.php ← Form schema, table schema, getPages()
No per-model controller needed! The framework's generic
ResourceRouteControllerhandles all CRUD routing automatically. You only need Resource + Page classes + Services.
# Multi-page mode (default) — generates List, Create, Edit pages
php artisan vuelament:resource Product --panel=Admin
# Single mode — modal-based create/edit (ManageProducts)
php artisan vuelament:resource Product --panel=Admin --simple
# Auto-generate form/table from database columns
php artisan vuelament:resource Product --panel=Admin --generate
This generates the full module structure:
app/Vuelament/Admin/Product/
├── Resources/
│ ├── ListProducts.php ← with CreateAction in getHeaderActions()
│ ├── CreateProduct.php
│ └── EditProduct.php
├── Pages/
├── Services/
│ └── ProductService.php
├── Widgets/
└── ProductResource.php ← with getPages() referencing page classes
# Basic — creates ProductService in the Product module
php artisan vuelament:service Product --panel=Admin
# With explicit resource module
php artisan vuelament:service Payment --panel=Admin --resource=Order
# Standalone page
php artisan vuelament:page Analytics --panel=Admin
# Page attached to a resource
php artisan vuelament:page Report --panel=Admin --resource=User
php artisan vuelament:panel Sales --id=sales
php artisan vuelament:user
Page classes define page-level actions via getHeaderActions(). This is identical to Filament's pattern.
// app/Vuelament/Admin/User/Resources/ListUsers.php
use ChristYoga123\Vuelament\Core\Pages\ListRecords;
use ChristYoga123\Vuelament\Components\Actions\CreateAction;
class ListUsers extends ListRecords
{
protected static ?string $resource = UserResource::class;
public static function getHeaderActions(): array
{
return [
CreateAction::make(),
// ExportAction::make(),
// ImportAction::make(),
];
}
}
Resources register their CRUD page classes and custom pages in getPages():
public static function getPages(): array
{
return [
// CRUD page classes — framework reads getHeaderActions() from these
'index' => Resources\ListUsers::class,
'create' => Resources\CreateUser::class,
'edit' => Resources\EditUser::class,
// Custom pages
'report' => Pages\ReportPage::route('/{record}/report'),
];
}
Table schemas contain only row-level inline actions. Page-level actions (like "Create") belong in getHeaderActions() on the page class.
public static function tableSchema(): PageSchema
{
return PageSchema::make()
->components([
Table::make()
->query(fn() => User::query()->latest())
->columns([
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email')->sortable()->searchable(),
ToggleColumn::make('is_active')->label('Active'),
])
->actions([
// ✅ Row-level actions (inline in table)
Action::make('deactivate')
->icon('user-x')
->color('warning')
->requiresConfirmation('Deactivate User', 'Are you sure?')
->action([UserService::class, 'deactivate']),
EditAction::make(),
DeleteAction::make(),
])
->bulkActions([
ActionGroup::make('Bulk Actions')
->icon('list')
->actions([
DeleteBulkAction::make(),
RestoreBulkAction::make(),
]),
])
->filters([
TrashFilter::make(),
])
->searchable()
->paginated()
->selectable(),
]);
}
use ChristYoga123\Vuelament\Components\Layout\Grid;
use ChristYoga123\Vuelament\Components\Layout\Section;
use ChristYoga123\Vuelament\Components\Form\TextInput;
use ChristYoga123\Vuelament\Components\Form\Toggle;
public static function formSchema(): PageSchema
{
return PageSchema::make()
->components([
Section::make('General Information')
->components([
Grid::make(2)->components([
TextInput::make('name')->required()->uniqueIgnoreRecord(),
TextInput::make('email')->email()->required()->uniqueIgnoreRecord(),
]),
Toggle::make('is_active')
->label('Status Active')
->hint('Turn off to prevent user login.'),
TextInput::make('password')
->password()
->revealable()
->dehydrateStateUsing(fn (string $state): string => Hash::make($state))
->saved(fn (?string $state): bool => filled($state))
->required(fn (string $operation): bool => $operation === 'create'),
])
]);
}
TextInput — text, email, password, number, with prefix/suffix and revealable supportTextarea — multi-line text inputRichEditor — Rich text editor (powered by VueQuill)DatePicker, TimePicker, DateRangePicker — Date/time selection (powered by VuePic)Select — Dropdown selectionCheckbox — Single or multiple checkbox groupRadio — Radio button group with horizontal/vertical layoutToggle — Switch toggle (powered by Shadcn Switch)FileInput — File upload with drag & drop, preview, and reorder supportRepeater — Dynamic repeatable field groupsUnlike Filament (where actions use Livewire Closures), Vuelament uses dedicated Service classes for business logic. This makes actions testable, reusable, and framework-agnostic.
Resource → defines actions with form + service callable
↓
Action → captures form data, resolves service from container
↓
Service → executes business logic (pure PHP, testable)
↓
Database → Eloquent operations
Action::make('deactivate')
->icon('user-x')
->color('warning')
->requiresConfirmation('Deactivate User', 'Are you sure?')
->action([UserService::class, 'deactivate'])
// app/Vuelament/Admin/User/Services/UserService.php
class UserService
{
public function deactivate(User $user, array $data = []): void
{
$user->update(['is_active' => false]);
}
}
The framework resolves the service instance from the container and injects dependencies:
$instance = app(UserService::class);
app()->call([$instance, 'deactivate'], [
'user' => $record, // injected by lowercase model basename
'record' => $record, // also available as generic 'record'
'data' => $formData, // form data from modal
]);
Action::make('assign_role')
->icon('shield')
->color('success')
->form([
Select::make('role')->options([
'admin' => 'Admin',
'editor' => 'Editor',
])->required(),
])
->action([UserService::class, 'assignRole'])
Vuelament evaluates form visibility, disabled, and required states entirely on the client — no server requests needed.
| Interaction | Filament (Livewire) | Vuelament (Vue 3 + Inertia) |
|---|---|---|
| Toggle field to show another | 🛑 Network request, re-renders component | ⚡ Instant. JavaScript only |
| Change required state based on selection | 🛑 Requires network request | ⚡ Instant. No server overhead |
| Simple validation visibility | 🛑 Network round-trip, can feel sluggish | ⚡ Instant. Zero latency |
Toggle::make('is_active')->label('Active Status'),
// Shows INSTANTLY when is_active is toggled on (no server request!)
TextInput::make('activation_code')
->visibleWhen('is_active', true)
->requiredWhen('is_active', true),
Select::make('type')->options([
'standard' => 'Standard',
'premium' => 'Premium',
]),
// Shows only when type = 'premium'
TextInput::make('premium_code')
->visibleWhen('type', 'premium'),
| Method | Description |
|---|---|
->visibleWhen('field', value) |
Show when field equals value |
->hiddenWhen('field', value) |
Hide when field equals value |
->disabledWhen('field', value) |
Disable when field equals value |
->enabledWhen('field', value) |
Enable when field equals value |
->requiredWhen('field', value) |
Required when field equals value |
->visibleWhenAll([...]) |
Show when ALL conditions match (AND logic) |
->visibleWhenAny([...]) |
Show when ANY condition matches (OR logic) |
===, !==, in, notIn, filled, blank, >, <, >=, <=
use ChristYoga123\Vuelament\Components\Table\Actions\Action;
Action::make('form')
->icon('form')
->color('success')
->label('Fill Form')
->modalHeading('Detailed User Form')
->modalWidth('4xl')
->modalCloseByClickingAway(false)
->modalCancelActionLabel('Close')
->form([
TextInput::make('reference_id')->required(),
Radio::make('type')->options([
'a' => 'Type A',
'b' => 'Type B',
])
])
->action([SomeService::class, 'processForm'])
->requiresConfirmation(): Auto-converts the form into a danger Action/Alert Dialog.->modalWidth('2xl'): Defines the modal width dynamically.->modalCancelAction(false) / ->modalSubmitAction(false): Hide specific modal buttons.->modalCloseByClickingAway(false): Prevents accidental closure from outside clicks.Vuelament supports multiple isolated admin panels by default.
Creating a new panel:
php artisan vuelament:panel Sales --id=sales
This creates App\Vuelament\Providers\SalesPanelProvider. Register in bootstrap/providers.php.
Creating resources for a specific panel:
php artisan vuelament:resource Invoice --panel=Sales
Vuelament isolates backend and frontend files into separate silos:
Backend: app/Vuelament/Sales/Invoice/InvoiceResource.php
resources/js/Pages/Vuelament/Sales/Resource/Invoice/...AdminPanelProvider and SalesPanelProvider have zero conflict.
By default, any authenticated user can access any panel. To restrict access (e.g., using roles), add a hasPanelAccess (or canAccessPanel) method your User model (app/Models/User.php):
use ChristYoga123\Vuelament\Core\Panel;
class User extends Authenticatable
{
public function hasPanelAccess(Panel $panel): bool
{
// Example: Only let users with role 'admin' access the Admin panel
if ($panel->getId() === 'admin') {
return $this->role === 'admin';
}
// Example: Only let 'sales' role access the Sales panel
if ($panel->getId() === 'sales') {
return $this->role === 'sales';
}
return true; // Default access
}
}
If a user tries to log in to a Panel they do not possess access to, Vuelament will gracefully prevent login and securely show a "You cannot access this panel" error.
Generate completely custom pages for charts, widgets, or dashboards:
php artisan vuelament:page Analytics --panel=Admin
This generates two files:
app/Vuelament/Admin/Pages/AnalyticsPage.phpresources/js/Pages/Vuelament/Admin/Pages/AnalyticsPage.vueuse ChristYoga123\Vuelament\Core\BasePage;
class AnalyticsPage extends BasePage
{
protected static ?string $navigationIcon = 'activity';
protected static ?string $navigationLabel = 'Live Analytics';
protected static string $slug = 'analytics';
public static function getData(?\Illuminate\Database\Eloquent\Model $record = null): array
{
return [
'totalUsers' => User::count(),
'revenue' => 5000000,
];
}
}
vue-quill, vue-datepicker) are isolated into chunks via Vite.resources/js/components/vuelament/
├── Table.vue # Main table orchestrator
├── table/
│ ├── utils.js # Pure helpers (resolveIcon, formatCell)
│ ├── composables/
│ │ └── useTableState.js # All reactive table state & logic
│ ├── columns/
│ │ ├── ColumnCell.vue # Dispatcher (selects by col.type)
│ │ ├── TextCell.vue # Text + prefix/suffix/color
│ │ ├── BadgeCell.vue # Badge with colors
│ │ ├── ToggleCell.vue # Shadcn Switch + loading
│ │ ├── CheckboxCell.vue # Checkbox + loading
│ │ ├── ImageCell.vue # Image with circle/thumbnail
│ │ └── IconCell.vue # Dynamic Lucide icon
│ ├── TableToolbar.vue # Search + filters + column toggle
│ ├── TableFiltersAbove.vue # Filters above table
│ ├── TableRowActions.vue # Row actions (edit, delete, custom)
│ ├── TablePagination.vue # Pagination + per page
│ ├── TableConfirmDialog.vue # Confirmation dialog
│ └── TableActionFormDialog.vue # Custom action form dialog
├── form/
│ ├── RichEditor.vue # Rich text editor wrapper
│ ├── DatePicker.vue # Date/time picker wrapper
│ └── composables/
│ └── useFormReactivity.js # Client-side form reactivity
useTableState, useFormReactivity) — extract all reactive logic from componentsColumnCell.vue routes to correct sub-component based on col.typevisibleWhen(), disabledWhen(), requiredWhen() methods serialized to JSON rulesPublish the config file:
php artisan vendor:publish --tag=vuelament-config
// config/vuelament.php
return [
'default_panel' => 'admin',
'user_model' => \App\Models\User::class,
'app_path' => 'Vuelament',
'assets' => [
'auto_publish' => true,
],
];
You can publish and customize the code generation stubs:
php artisan vendor:publish --tag=vuelament-stubs
Stubs will be published to stubs/vuelament/. The framework will prioritize custom stubs over package defaults.
| Command | Description |
|---|---|
vuelament:install |
Install Vuelament — publish assets, scaffold, setup |
vuelament:resource {Name} |
Generate full CRUD module (Resource + Pages + Service) |
vuelament:service {Name} |
Generate a Service class for action business logic |
vuelament:page {Name} |
Generate a custom page (PHP class + Vue component) |
vuelament:panel {Name} |
Generate a new panel provider |
vuelament:user |
Create an admin user |
| Option | Description | Available On |
|---|---|---|
--panel=Admin |
Target panel (default: Admin) | resource, service, page |
--resource=User |
Attach to resource module | service, page |
--simple |
Single-mode (modal create/edit) | resource |
--generate |
Auto-generate from database | resource |
--force |
Overwrite existing files | all |
Vuelament is open-sourced software licensed under the MIT License.
Happy Coding! 🎉