Jun 4, 2023 10 min read

Laravel with AdminLTE & Multi-Auth — Laravel 11 Guide

# Setting Up Laravel with AdminLTE and Multi-Auth (Laravel 11 Guide) If you're building an admin panel in Laravel, you've probably heard about AdminLTE — it's one of the most popular admin dashboard templates out there. Combine it with multi-authentication (separate dashboards for admins and regula

AdminLTE admin dashboard running inside a Laravel 11 project

If you're building an admin panel in Laravel, you've probably heard about AdminLTE — it's one of the most popular admin dashboard templates out there. Combine it with multi-authentication (separate dashboards for admins and regular users), and you've got the backbone of most SaaS apps and membership sites. For more details, check out Step-by-Step Guide: Setting Up Laravel with AdminLTE and Mul. For more details, check out Setting Up a Python Development Environment on VirtualBox wi. For more details, check out Building a Real-Time Chat App with Laravel Livewire and Push.

I've been using this stack for years across several projects, and I'll walk you through the exact setup I use. This guide targets Laravel 11 (the latest), but the principles apply to Laravel 10 as well.

What We're Building

Laravel AdminLTE admin dashboard interface

By the end of this guide, you'll have:

  • A fresh Laravel 11 installation
  • AdminLTE 3 integrated as the admin theme
  • Multi-authentication with role-based views (admin vs. user)
  • Cleaner route organization and middleware guarding
  • A solid foundation you can drop into any project

Let's get to it.

Prerequisites

Before we start, make sure you have:

  • PHP 8.2+ and Composer installed
  • Node.js 18+ and npm (for compiling AdminLTE assets)
  • Basic Laravel familiarity (routes, controllers, migrations)
  • A database set up (MySQL, PostgreSQL, or SQLite — your pick)

Step 1: Create a Fresh Laravel 11 Project

Fire up your terminal and run:

composer create-project laravel/laravel adminlte-app\ncd adminlte-app\n

Laravel 11 ships with a leaner structure than previous versions — no more app/Http/Kernel.php or routes/api.php by default. You'll add middleware directly in route files using Laravel's new bootstrap approach, which I'll show in a bit.

Once that's done, verify your .env file has your database credentials set up. For a quick dev setup, SQLite works great:

DB_CONNECTION=sqlite\nDB_DATABASE=/absolute/path/to/adminlte-app/database/database.sqlite\n

Step 2: Install and Configure AdminLTE

AdminLTE 3 uses Bootstrap 4, Font Awesome, and a bunch of plugins. We'll install it via npm and compile the assets with Vite (Laravel 11's default build tool).

npm install admin-lte@^3.2 --save-dev\nnpm install\n

AdminLTE 3 ships assets under node_modules/admin-lte/dist/ and node_modules/admin-lte/plugins/. We need to copy or reference them. Since Laravel 11 uses Vite (not Mix like older versions), we'll configure it in vite.config.js:

import { defineConfig } from 'vite';\nimport laravel from 'laravel-vite-plugin';\nimport path from 'path';\n\nexport default defineConfig({\n    plugins: [\n        laravel({\n            input: ['resources/css/app.css', 'resources/js/app.js'],\n            refresh: true,\n        }),\n    ],\n    resolve: {\n        alias: {\n            '~admin-lte': path.resolve(__dirname, 'node_modules/admin-lte'),\n        },\n    },\n});\n

Now in your resources/js/app.js, import AdminLTE:

import './bootstrap';\nimport 'admin-lte/dist/js/adminlte.min.js';\n

And in resources/css/app.css, import the styles:

@import 'admin-lte/dist/css/adminlte.min.css';\n@import 'admin-lte/plugins/fontawesome-free/css/all.min.css';\n@import 'admin-lte/plugins/overlayScrollbars/css/OverlayScrollbars.min.css';\n

Build the assets:

npm run build\n

You've now got AdminLTE assets compiled. We'll wire them into your layout in a moment.

Step 3: Set Up User Authentication with Roles

Laravel 11 doesn't ship with make:auth anymore — that was removed years ago. Instead, we'll build a lightweight role-based auth system manually. This is cleaner than pulling in a full starter kit when all you need is an admin/user split.

Add a Role Column to Users

Create a migration to add a role field:

php artisan make:migration add_role_to_users_table\n

In the migration:

public function up(): void\n{\n    Schema::table('users', function (Blueprint $table) {\n        $table->string('role')->default('user')->after('email');\n    });\n}\n\npublic function down(): void\n{\n    Schema::table('users', function (Blueprint $table) {\n        $table->dropColumn('role');\n    });\n}\n

Run the migration:

php artisan migrate\n

Seed an Admin User

Add a seeder so you don't have to manually create users every time. Open database/seeders/DatabaseSeeder.php:

public function run(): void\n{\n    \\App\\Models\\User::factory()->create([\n        'name' => 'Admin User',\n        'email' => 'admin@example.com',\n        'password' => bcrypt('password'),\n        'role' => 'admin',\n    ]);\n\n    \\App\\Models\\User::factory()->create([\n        'name' => 'Regular User',\n        'email' => 'user@example.com',\n        'password' => bcrypt('password'),\n        'role' => 'user',\n    ]);\n}\n

Run:

php artisan db:seed\n

Step 4: Create the Role Middleware

Create a middleware that checks the user's role:

php artisan make:middleware RoleMiddleware\n

In app/Http/Middleware/RoleMiddleware.php:

<?php\n\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass RoleMiddleware\n{\n    public function handle(Request $request, Closure $next, string $role): Response\n    {\n        if (! $request->user() || $request->user()->role !== $role) {\n            abort(403, 'Unauthorized action.');\n        }\n\n        return $next($request);\n    }\n}\n

In Laravel 11, you register middleware aliases in bootstrap/app.php:

return Application::configure(basePath: dirname(__DIR__))\n    ->withRouting(\n        web: __DIR__.'/../routes/web.php',\n        commands: __DIR__.'/../routes/console.php',\n        health: '/up',\n    )\n    ->withMiddleware(function (Middleware $middleware) {\n        $middleware->alias([\n            'role' => \\App\\Http\\Middleware\\RoleMiddleware::class,\n        ]);\n    })\n    ->create();\n

Step 5: Organize Routes by Role

Now create separate route groups for admins and users in routes/web.php:

<?php\n\nuse App\\Http\\Controllers\\AdminController;\nuse App\\Http\\Controllers\\UserController;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::get('/', function () {\n    return view('welcome');\n});\n\n// Auth routes (login, register) — use Laravel's built-in auth\nRoute::middleware('guest')->group(function () {\n    Route::view('/login', 'auth.login')->name('login');\n    Route::view('/register', 'auth.register')->name('register');\n});\n\n// Admin routes\nRoute::middleware(['auth', 'role:admin'])->prefix('admin')->name('admin.')->group(function () {\n    Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('dashboard');\n});\n\n// User routes\nRoute::middleware(['auth', 'role:user'])->prefix('user')->name('user.')->group(function () {\n    Route::get('/dashboard', [UserController::class, 'dashboard'])->name('dashboard');\n});\n

Step 6: Build Role-Based Controllers

Generate the controllers:

php artisan make:controller AdminController\nphp artisan make:controller UserController\n

AdminController:

<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\n\nclass AdminController extends Controller\n{\n    public function dashboard()\n    {\n        return view('admin.dashboard');\n    }\n}\n

UserController:

<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\n\nclass UserController extends Controller\n{\n    public function dashboard()\n    {\n        return view('user.dashboard');\n    }\n}\n

Step 7: Create the AdminLTE Layout and Views

Let's wire up AdminLTE as the admin layout. Create resources/views/layouts/admin.blade.php:

<!DOCTYPE html>\n<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">\n<head>\n    <meta charset="utf-8">\n    <meta name="viewport" content="width=device-width, initial-scale=1">\n    <title>@yield('title', 'Admin Panel') - {{ config('app.name') }}</title>\n    @vite(['resources/css/app.css'])\n</head>\n<body class="hold-transition sidebar-mini layout-fixed">\n<div class="wrapper">\n\n    <!-- Navbar -->\n    <nav class="main-header navbar navbar-expand navbar-white navbar-light">\n        <ul class="navbar-nav">\n            <li class="nav-item">\n                <a class="nav-link" data-widget="pushmenu" href="#" role="button">\n                    <i class="fas fa-bars"></i>\n                </a>\n            </li>\n        </ul>\n        <ul class="navbar-nav ml-auto">\n            <li class="nav-item">\n                <form method="POST" action="{{ route('logout') }}">\n                    @csrf\n                    <button type="submit" class="btn btn-danger">Logout</button>\n                </form>\n            </li>\n        </ul>\n    </nav>\n\n    <!-- Sidebar -->\n    <aside class="main-sidebar sidebar-dark-primary elevation-4">\n        <a href="#" class="brand-link">\n            <span class="brand-text font-weight-light">Admin Panel</span>\n        </a>\n        <div class="sidebar">\n            <nav class="mt-2">\n                <ul class="nav nav-pills nav-sidebar flex-column">\n                    <li class="nav-item">\n                        <a href="{{ route('admin.dashboard') }}" class="nav-link">\n                            <i class="nav-icon fas fa-tachometer-alt"></i>\n                            <p>Dashboard</p>\n                        </a>\n                    </li>\n                </ul>\n            </nav>\n        </div>\n    </aside>\n\n    <!-- Content Wrapper -->\n    <div class="content-wrapper">\n        <div class="content-header">\n            <div class="container-fluid">\n                <div class="row mb-2">\n                    <div class="col-sm-6">\n                        <h1 class="m-0">@yield('header')</h1>\n                    </div>\n                </div>\n            </div>\n        </div>\n        <section class="content">\n            <div class="container-fluid">\n                @yield('content')\n            </div>\n        </section>\n    </div>\n\n    <footer class="main-footer">\n        <strong>&copy; {{ date('Y') }} {{ config('app.name') }}.</strong> All rights reserved.\n    </footer>\n</div>\n@vite(['resources/js/app.js'])\n</body>\n</html>\n

Now create the admin dashboard view at resources/views/admin/dashboard.blade.php:

@extends('layouts.admin')\n\n@section('title', 'Admin Dashboard')\n@section('header', 'Admin Dashboard')\n\n@section('content')\n<div class="row">\n    <div class="col-lg-3 col-6">\n        <div class="small-box bg-info">\n            <div class="inner">\n                <h3>150</h3>\n                <p>New Orders</p>\n            </div>\n            <div class="icon"><i class="fas fa-shopping-cart"></i></div>\n        </div>\n    </div>\n    <div class="col-lg-3 col-6">\n        <div class="small-box bg-success">\n            <div class="inner">\n                <h3>53<sup style="font-size: 20px">%</sup></h3>\n                <p>Bounce Rate</p>\n            </div>\n            <div class="icon"><i class="fas fa-chart-pie"></i></div>\n        </div>\n    </div>\n</div>\n@endsection\n

Create a similar view for user dashboards at resources/views/user/dashboard.blade.php — keep it simpler for regular users.

Step 8: Implement Login and Registration

You'll need login/register views. Create resources/views/auth/login.blade.php:

<!DOCTYPE html>\n<html>\n<head>\n    <meta charset="utf-8">\n    <meta name="viewport" content="width=device-width, initial-scale=1">\n    <title>Login - {{ config('app.name') }}</title>\n    @vite(['resources/css/app.css'])\n</head>\n<body class="hold-transition login-page">\n<div class="login-box">\n    <div class="login-logo"><b>Admin</b> App</div>\n    <div class="card">\n        <div class="card-body login-card-body">\n            <p class="login-box-msg">Sign in to start</p>\n            <form method="POST" action="{{ route('login') }}">\n                @csrf\n                <div class="input-group mb-3">\n                    <input type="email" name="email" class="form-control" placeholder="Email" required>\n                    <div class="input-group-append"><div class="input-group-text"><i class="fas fa-envelope"></i></div></div>\n                </div>\n                <div class="input-group mb-3">\n                    <input type="password" name="password" class="form-control" placeholder="Password" required>\n                    <div class="input-group-append"><div class="input-group-text"><i class="fas fa-lock"></i></div></div>\n                </div>\n                <button type="submit" class="btn btn-primary btn-block">Sign In</button>\n            </form>\n        </div>\n    </div>\n</div>\n@vite(['resources/js/app.js'])\n</body>\n</html>\n

For authentication logic, you can use a simple custom LoginController, or install Laravel Breeze for the full auth scaffolding and then wrap it with AdminLTE. The cleanest approach is a custom controller that redirects based on role after login:

php artisan make:controller Auth\\LoginController\n
<?php\n\nnamespace App\\Http\\Controllers\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\n\nclass LoginController extends Controller\n{\n    public function showLoginForm()\n    {\n        return view('auth.login');\n    }\n\n    public function login(Request $request)\n    {\n        $credentials = $request->validate([\n            'email' => 'required|email',\n            'password' => 'required',\n        ]);\n\n        if (Auth::attempt($credentials, $request->filled('remember'))) {\n            $request->session()->regenerate();\n\n            // Redirect based on role\n            if (Auth::user()->role === 'admin') {\n                return redirect()->route('admin.dashboard');\n            }\n            return redirect()->route('user.dashboard');\n        }\n\n        return back()->withErrors(['email' => 'Invalid credentials.']);\n    }\n\n    public function logout(Request $request)\n    {\n        Auth::logout();\n        $request->session()->invalidate();\n        $request->session()->regenerateToken();\n        return redirect('/');\n    }\n}\n

Wire the routes in routes/web.php:

use App\\Http\\Controllers\\Auth\\LoginController;\n\nRoute::controller(LoginController::class)->group(function () {\n    Route::get('/login', 'showLoginForm')->name('login');\n    Route::post('/login', 'login');\n    Route::post('/logout', 'logout')->name('logout');\n});\n

Testing the Setup

Start the dev server:

php artisan serve\n

Log in with: - Admin: admin@example.com / password → should redirect to /admin/dashboard - User: user@example.com / password → should redirect to /user/dashboard

Try visiting /admin/dashboard as a regular user — you should get a 403 error. That's the role middleware working.

Troubleshooting Common Issues

403 on Every Route

Check that your bootstrap/app.php has the middleware alias registered. Without it, the role: middleware alias won't resolve.

AdminLTE Styles Not Loading

Run npm run build and make sure you're using @vite() in your layouts, not @vite (note the parentheses — they're required in Blade).

Login Not Redirecting Correctly

Verify the role column in your users table has the correct string value. Typos like "admin " (trailing space) will cause middleware to fail.

Next Steps

This setup gives you a solid foundation. From here you can:

  • Add a permissions package like spatie/laravel-permission for granular access control
  • Create separate admin and user resource controllers
  • Customize the AdminLTE sidebar with dynamic menus based on permissions
  • Set up API authentication with Sanctum for token-based access

The combination of Laravel 11 + AdminLTE + role-based auth is battle-tested and scales from small projects to full enterprise apps. Drop a comment if you hit any snags during setup.

Techie Mike
Techie Mike
Computer Science teacher in Thailand. 10+ years Cambridge IGCSE, 4 years AS/A Level. BSc Computer Science & Engineering. Ex-Intel, Virgin Media. Practical exam prep, past paper walkthroughs and tech tutorials.