Transfer the parameters from the old config (moonshine_old.php) to the new one. See the documentation on configuration.
The menu structure has changed in the new version:
Open app/MoonShine/Layouts/MoonShineLayout.php.
Copy the old menu from MoonShineServiceProvider_old.php to the menu method.
Remove the heroicons.outline. prefix from icons.
Update all resource instances to string classes:
// Was
MenuItem::make('Settings', new SettingResource(), 'heroicons.outline.adjustments-vertical')
// Now
MenuItem::make('Settings', SettingResource::class, 'adjustments-vertical')
// Was
MenuItem::make('Settings', new SettingResource(), 'heroicons.outline.adjustments-vertical')
// Now
MenuItem::make('Settings', SettingResource::class, 'adjustments-vertical')
3. Register Resources and Pages
In the new MoonShineServiceProvider.php, you need to register all resources and pages:
$core->resources([
MoonShineUserResource::class,
MoonShineUserRoleResource::class,
// Add all your resources
]);
$core->pages([
...$config->getPages(),
SettingPage::class,
]);
$core->resources([
MoonShineUserResource::class,
MoonShineUserRoleResource::class,
// Add all your resources
]);
$core->pages([
...$config->getPages(),
SettingPage::class,
]);
$core->resources([
MoonShineUserResource::class,
MoonShineUserRoleResource::class,
// Add all your resources
]);
$core->pages([
...$config->getPages(),
SettingPage::class,
]);
Commands to generate lists:
For importing namespaces:
find app/MoonShine/Resources -type f | sed "s/app/use App/" | sed "s|/|\\\|g" | sed "s/.php/;/" | sort
find app/MoonShine/Resources -type f -exec basename {} \; | sed "s/.php/::class,/" | sort
4. Update Dashboard
Move required components from Dashboard_old.php to the new Dashboard.php.
Take note of the changes in the Changelog section.
5. Remove Old Files
After successful migration, remove:
# Old Layout (if it exists)
rm app/MoonShine/MoonShineLayout.php
# Backups of files from 2.x
rm config/moonshine_old.php
rm app/Providers/MoonShineServiceProvider_old.php
rm app/MoonShine/Pages/Dashboard_old.php
# Old Layout (if it exists)
rmapp/MoonShine/MoonShineLayout.php
# Backups of files from 2.x
rmconfig/moonshine_old.php
rmapp/Providers/MoonShineServiceProvider_old.php
rmapp/MoonShine/Pages/Dashboard_old.php
# Old Layout (if it exists)
rm app/MoonShine/MoonShineLayout.php
# Backups of files from 2.x
rm config/moonshine_old.php
rm app/Providers/MoonShineServiceProvider_old.php
rm app/MoonShine/Pages/Dashboard_old.php
// Was
new NameResource()
// Now
// Recommended via DI
// or:
app(NameResource::class)
// Was
newNameResource()
// Now
// Recommended via DI
// or:
app(NameResource::class)
// Was
new NameResource()
// Now
// Recommended via DI
// or:
app(NameResource::class)
Method signatures:
// Was
public function components(): array
public function title(): string
public function breadcrumbs(): string
public function rules(Model $item): array
protected function afterUpdated(Model $user): Model
public function detailButtons(): array
public function modifyListComponent(MoonShineRenderable|TableBuilder $table): MoonShineRenderable
$field->getData()
detailPageUrl
MoonShineAuth::guard()
getActiveActions()
// Now
protected function components(): iterable
public function getTitle(): string
public function getBreadcrumbs(): string
protected function rules($item): array
protected function afterUpdated($user): Model
public function detailButtons(): ListOf
public function modifyListComponent(ComponentContract $table): ComponentContract
$field->getData()->getOriginal()
getDetailPageUrl
MoonShineAuth::getGuard()
activeActions()
// Was
public function components(): array
public function title(): string
public function breadcrumbs(): string
public function rules(Model $item): array
protected function afterUpdated(Model $user): Model
public function detailButtons(): array
public function modifyListComponent(MoonShineRenderable|TableBuilder $table): MoonShineRenderable
$field->getData()
detailPageUrl
MoonShineAuth::guard()
getActiveActions()
// Now
protected function components(): iterable
public function getTitle(): string
public function getBreadcrumbs(): string
protected function rules($item): array
protected function afterUpdated($user): Model
public function detailButtons(): ListOf
public function modifyListComponent(ComponentContract $table): ComponentContract
$field->getData()->getOriginal()
getDetailPageUrl
MoonShineAuth::getGuard()
activeActions()
Changes in field methods:
// Was
public function fields(): array
// Now
protected function indexFields(): iterable // only accepts fields
protected function detailFields(): iterable
protected function formFields(): iterable
// Was
publicfunctionfields():array
// Now
protectedfunctionindexFields():iterable// only accepts fields
protectedfunctiondetailFields():iterable
protectedfunctionformFields():iterable
// Was
public function fields(): array
// Now
protected function indexFields(): iterable // only accepts fields
protected function detailFields(): iterable
protected function formFields(): iterable
Table attributes:
// New format
TableBuilder::make()
->tdAttributes(fn(mixed $data, int $row, TableBuilder $table): array =>
$row === 3 ? ['class' => 'bgc-yellow'] : []
)
->tdAttributes(fn(mixed $data, int $row, int $cell, TableBuilder $table): array =>
$cell === 3 ? ['align' => 'right'] : []
)
// New format
TableBuilder::make()
->tdAttributes(fn(mixed $data, int $row, TableBuilder $table):array =>
$row ===3? ['class'=>'bgc-yellow'] : []
)
->tdAttributes(fn(mixed $data, int $row, int $cell, TableBuilder $table):array =>
$cell ===3? ['align'=>'right'] : []
)
// New format
TableBuilder::make()
->tdAttributes(fn(mixed $data, int $row, TableBuilder $table): array =>
$row === 3 ? ['class' => 'bgc-yellow'] : []
)
->tdAttributes(fn(mixed $data, int $row, int $cell, TableBuilder $table): array =>
$cell === 3 ? ['align' => 'right'] : []
)
Changes in other methods:
Helper to_page → toPage
Instead of the columnSpan method in components, use the component method Column: Column::make([...])->columnSpan(..)
Instead of expansion('url'), use the suffix('url') method
Remove the heroicons.outline and heroicons.solid prefixes.
These icons are now available by default.
Menu:
// Was
MenuItem::make('Settings', new SettingResource(), 'heroicons.outline.adjustments-vertical')
// Now
MenuItem::make('Settings', SettingResource::class, 'adjustments-vertical')
// Was
MenuItem::make('Settings', new SettingResource(), 'heroicons.outline.adjustments-vertical')
// Now
MenuItem::make('Settings', SettingResource::class, 'adjustments-vertical')
Asynchronous events:
// Was
->async(asyncUrl: ..., asyncEvents: ...)
'table-updated-{name}'
// Now
->async(url: ..., events: ...)
AlpineJs::event(JsEvent::TABLE_UPDATED, {name})
// Was
->async(asyncUrl: ..., asyncEvents: ...)
'table-updated-{name}'
// Now
->async(url: ..., events: ...)
AlpineJs::event(JsEvent::TABLE_UPDATED, {name})
// Was
->async(asyncUrl: ..., asyncEvents: ...)
'table-updated-{name}'
// Now
->async(url: ..., events: ...)
AlpineJs::event(JsEvent::TABLE_UPDATED, {name})
Sort direction:
// Was
protected string $sortDirection = 'ASC';
// Now
protected SortDirection $sortDirection = SortDirection::ASC;
// Was
protected string $sortDirection = 'ASC';
// Now
protected SortDirection $sortDirection = SortDirection::ASC;
Assets:
// Was
$assets // strings
// Now
$assets // accepts AssetElementContract, such as Css, InlineCss, Js, InlineJs
For management, use [AssetManager](/docs/3.x/appearance/assets).
// Was
$assets // strings
// Now
$assets // accepts AssetElementContract, such as Css, InlineCss, Js, InlineJs
Formanagement, use [AssetManager](/docs/3.x/appearance/assets).
// Was
$assets // strings
// Now
$assets // accepts AssetElementContract, such as Css, InlineCss, Js, InlineJs
For management, use [AssetManager](/docs/3.x/appearance/assets).
Removed Variables
protected bool $isAsync = true; (now enabled by default)