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

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\nLaravel 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\nStep 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\nAdminLTE 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});\nNow in your resources/js/app.js, import AdminLTE:
import './bootstrap';\nimport 'admin-lte/dist/js/adminlte.min.js';\nAnd 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';\nBuild the assets:
npm run build\nYou'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\nIn 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}\nRun the migration:
php artisan migrate\nSeed 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}\nRun:
php artisan db:seed\nStep 4: Create the Role Middleware
Create a middleware that checks the user's role:
php artisan make:middleware RoleMiddleware\nIn 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}\nIn 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();\nStep 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});\nStep 6: Build Role-Based Controllers
Generate the controllers:
php artisan make:controller AdminController\nphp artisan make:controller UserController\nAdminController:
<?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}\nUserController:
<?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}\nStep 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>© {{ date('Y') }} {{ config('app.name') }}.</strong> All rights reserved.\n </footer>\n</div>\n@vite(['resources/js/app.js'])\n</body>\n</html>\nNow 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\nCreate 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>\nFor 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}\nWire 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});\nTesting the Setup
Start the dev server:
php artisan serve\nLog 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-permissionfor 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.