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
cd adminlte-app
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
DB_DATABASE=/absolute/path/to/adminlte-app/database/database.sqlite
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
npm install
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';
import laravel from 'laravel-vite-plugin';
import path from 'path';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
resolve: {
alias: {
'~admin-lte': path.resolve(__dirname, 'node_modules/admin-lte'),
},
},
});
Now in your resources/js/app.js, import AdminLTE:
import './bootstrap';
import 'admin-lte/dist/js/adminlte.min.js';
And in resources/css/app.css, import the styles:
@import 'admin-lte/dist/css/adminlte.min.css';
@import 'admin-lte/plugins/fontawesome-free/css/all.min.css';
@import 'admin-lte/plugins/overlayScrollbars/css/OverlayScrollbars.min.css';
Build the assets:
npm run build
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
In the migration:
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('user')->after('email');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
Run the migration:
php artisan migrate
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
{
\App\Models\User::factory()->create([
'name' => 'Admin User',
'email' => '[email protected]',
'password' => bcrypt('password'),
'role' => 'admin',
]);
\App\Models\User::factory()->create([
'name' => 'Regular User',
'email' => '[email protected]',
'password' => bcrypt('password'),
'role' => 'user',
]);
}
Run:
php artisan db:seed
Step 4: Create the Role Middleware
Create a middleware that checks the user's role:
php artisan make:middleware RoleMiddleware
In app/Http/Middleware/RoleMiddleware.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class RoleMiddleware
{
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user() || $request->user()->role !== $role) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
In Laravel 11, you register middleware aliases in bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'role' => \App\Http\Middleware\RoleMiddleware::class,
]);
})
->create();
Step 5: Organize Routes by Role
Now create separate route groups for admins and users in routes/web.php:
<?php
use App\Http\Controllers\AdminController;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
// Auth routes (login, register) — use Laravel's built-in auth
Route::middleware('guest')->group(function () {
Route::view('/login', 'auth.login')->name('login');
Route::view('/register', 'auth.register')->name('register');
});
// Admin routes
Route::middleware(['auth', 'role:admin'])->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('dashboard');
});
// User routes
Route::middleware(['auth', 'role:user'])->prefix('user')->name('user.')->group(function () {
Route::get('/dashboard', [UserController::class, 'dashboard'])->name('dashboard');
});
Step 6: Build Role-Based Controllers
Generate the controllers:
php artisan make:controller AdminController
php artisan make:controller UserController
AdminController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
public function dashboard()
{
return view('admin.dashboard');
}
}
UserController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function dashboard()
{
return view('user.dashboard');
}
}
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>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Admin Panel') - {{ config('app.name') }}</title>
@vite(['resources/css/app.css'])
</head>
<body class="hold-transition sidebar-mini layout-fixed">
<div class="wrapper">
<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#" role="button">
<i class="fas fa-bars"></i>
</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="btn btn-danger">Logout</button>
</form>
</li>
</ul>
</nav>
<!-- Sidebar -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<a href="#" class="brand-link">
<span class="brand-text font-weight-light">Admin Panel</span>
</a>
<div class="sidebar">
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column">
<li class="nav-item">
<a href="{{ route('admin.dashboard') }}" class="nav-link">
<i class="nav-icon fas fa-tachometer-alt"></i>
<p>Dashboard</p>
</a>
</li>
</ul>
</nav>
</div>
</aside>
<!-- Content Wrapper -->
<div class="content-wrapper">
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">@yield('header')</h1>
</div>
</div>
</div>
</div>
<section class="content">
<div class="container-fluid">
@yield('content')
</div>
</section>
</div>
<footer class="main-footer">
<strong>© {{ date('Y') }} {{ config('app.name') }}.</strong> All rights reserved.
</footer>
</div>
@vite(['resources/js/app.js'])
</body>
</html>
Now create the admin dashboard view at resources/views/admin/dashboard.blade.php:
@extends('layouts.admin')
@section('title', 'Admin Dashboard')
@section('header', 'Admin Dashboard')
@section('content')
<div class="row">
<div class="col-lg-3 col-6">
<div class="small-box bg-info">
<div class="inner">
<h3>150</h3>
<p>New Orders</p>
</div>
<div class="icon"><i class="fas fa-shopping-cart"></i></div>
</div>
</div>
<div class="col-lg-3 col-6">
<div class="small-box bg-success">
<div class="inner">
<h3>53<sup style="font-size: 20px">%</sup></h3>
<p>Bounce Rate</p>
</div>
<div class="icon"><i class="fas fa-chart-pie"></i></div>
</div>
</div>
</div>
@endsection
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>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Login - {{ config('app.name') }}</title>
@vite(['resources/css/app.css'])
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo"><b>Admin</b> App</div>
<div class="card">
<div class="card-body login-card-body">
<p class="login-box-msg">Sign in to start</p>
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="input-group mb-3">
<input type="email" name="email" class="form-control" placeholder="Email" required>
<div class="input-group-append"><div class="input-group-text"><i class="fas fa-envelope"></i></div></div>
</div>
<div class="input-group mb-3">
<input type="password" name="password" class="form-control" placeholder="Password" required>
<div class="input-group-append"><div class="input-group-text"><i class="fas fa-lock"></i></div></div>
</div>
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
</div>
@vite(['resources/js/app.js'])
</body>
</html>
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
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
public function showLoginForm()
{
return view('auth.login');
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (Auth::attempt($credentials, $request->filled('remember'))) {
$request->session()->regenerate();
// Redirect based on role
if (Auth::user()->role === 'admin') {
return redirect()->route('admin.dashboard');
}
return redirect()->route('user.dashboard');
}
return back()->withErrors(['email' => 'Invalid credentials.']);
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}
Wire the routes in routes/web.php:
use App\Http\Controllers\Auth\LoginController;
Route::controller(LoginController::class)->group(function () {
Route::get('/login', 'showLoginForm')->name('login');
Route::post('/login', 'login');
Route::post('/logout', 'logout')->name('logout');
});
Testing the Setup
Start the dev server:
php artisan serve
Log in with: - Admin: [email protected] / password → should redirect to /admin/dashboard - User: [email protected] / 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.