Jun 4, 2023 9 min read

Setting Up Laravel with AdminLTE and 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

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 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
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>&copy; {{ 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-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.