EN
Getting started

Your first page

This page shows how to add a new admin page to the theme: a view, a route and a menu entry.

Quick example

{{-- resources/views/admin/customers/index.blade.php --}}
@extends('layouts.admin.vertical')

@section('title', __('Customers'))

@section('content')
    <acun:page-header :title="__('Customers')" />
@endsection
// routes/web.php
Route::view('/customers', 'admin.customers.index')->name('customers.index');

Open the URL: the page appears inside the panel with its menu, and the tab title is "Customers". In the theme, the panel routes are in a Route::prefix('admin') group. If you put the route inside that group, the URL is /admin/customers.

Step by step

The example adds a "Customers" page to the panel. The same steps apply to every new page.

  1. Create the view. Panel pages live under resources/views/admin/:

    {{-- resources/views/admin/customers/index.blade.php --}}
    @extends('layouts.admin.vertical')
    
    @section('title', __('Customers'))
    
    @section('content')
        <section class="mx-auto w-full max-w-7xl space-y-6">
            <acun:page-header
                :title="__('Customers')"
                :breadcrumbs="[['label' => __('Records')], ['label' => __('Customers')]]"
            />
    
            <acun:card>
                <acun:empty-state :title="__('No customers yet')" :description="__('Get started by adding your first customer.')">
                    <acun:slot:action><acun:button>{{ __('New customer') }}</acun:button></acun:slot:action>
                </acun:empty-state>
            </acun:card>
        </section>
    @endsection
    

    @section('title') sets the tab title. @section('content') goes into the content area in the middle of the layout.

  2. Add the route. If the page shows no data, Route::view is enough:

    // routes/web.php, inside the Route::prefix('admin') group
    Route::view('/customers', 'admin.customers.index')->name('customers.index');
    

    If you need data from the database, write a controller. The controller returns the view with view('admin.customers.index', [...]), and the route becomes Route::get('/customers', CustomerController::class).

  3. Add a line to the menu:

    { "title": "Customers", "icon": "users", "route": "customers.index", "active": "customers.*" }
    

    Put the line in the menu array of the resources/menu/main.json file. The page appears both in the menu and in the ⌘K search. Thanks to active, the item also stays selected on subpages such as customers.create.

Changing the layout

Change the layout name in the @extends line:

Layout When
layouts.admin.vertical Vertical menu on the left; for most pages.
layouts.admin.horizontal Menu in the top bar.
layouts.admin.blank No menu: sign-in, registration and error screens.

The Layouts page describes what each layout contains and its options.

Livewire page

If the page is interactive (a form, live search), write it as a Livewire component:

// app/Livewire/Customers/Index.php
namespace App\Livewire\Customers;

use Illuminate\Contracts\View\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.admin.vertical')]
#[Title('Customers')]
final class Index extends Component
{
    public function render(): View
    {
        return view('admin.customers.index');
    }
}
{{-- resources/views/admin/customers/index.blade.php --}}
<section class="mx-auto w-full max-w-7xl space-y-6">
    <acun:page-header :title="__('Customers')" />
    …
</section>
// routes/web.php
use App\Livewire\Customers\Index as CustomersIndex;

Route::livewire('/customers', CustomersIndex::class)->name('customers.index');

#[Layout] places the page inside the layout. #[Title] is the translation key of the tab title; a PHP attribute can't call __(), so the layout translates it. Don't write @extends or @section in a Livewire view. The view starts with a single root element, <section> in the example.

Page-specific script

Push a JavaScript file that should run only on this page to the page-scripts stack:

@push('page-scripts')
    @vite('resources/js/pages/customers.js')
@endpush

The layouts print this stack at the end of the page. The CSS, JavaScript and build page explains how to add the file to the Vite inputs.

Authorization

The menu doesn't check permissions. Protect pages that require authorization with your own middleware or policy on the route. To hide the item from the menu, see the Menu page.

Learn more

  • List pages: turn the page into a list with search, filters and bulk actions.
  • Form modal: a modal for adding and editing records.
  • Menu: every field of the menu file.
  • Layouts: layout files, slots and props.
Acun UIDesigned for people.