Build modern authentication screens with Laravel 8 and Bootstrap 5
In this tutorial, I will show you how to get started fast with Laravel 8 and build modern authentication pages using Bootstrap 5.
I am Laravel and Bootstrap fan since the beginning. Even though recently Laravel switch to Tailwind CSS as its primary way of building interfaces, I still prefer Bootstrap for several reasons.
- I like my HTML and CSS separated
- Building or maintaining components and pages is much easier
- Basic customization is made using Sass which makes global customization less time consuming
- You can always use Bootstrap utilities to extend components directly into your HTML or add new ones just like Tailwind
In this tutorial, I will show you how to get started fast with Laravel 8 and Bootstrap 5. These are the things we'll cover:
- Installation
- Laravel Mix setup
- Authentication scaffolding
- Login, Register, and Recover design
- User management
Also, I am going to use a pretty cool design system built with Bootstrap 5 to extend the default look and feel and add new components and utility classes for allowing me to build more complex architectures.
If you want your application's UI to look like the one in the article's image, then you should give Webpixels CSS a try.
Installation
I won't go into detail and write about the requirements and steps you need to take before installing Laravel. This information already exists in the official documentation
Now, if you're ready, let's dive in.
Create a new folder called laravel-starter-kit
and install Laravel using your preferred method from the docs.
Next, we will install everything we need for our app.
Authentication Scaffolding
Laravel provides a basic starting point using Bootstrap, React, and/or Vue that will be helpful for many applications.
By default, Laravel uses NPM to install both of these frontend packages.
composer require laravel/ui
Generate basic scaffolding...
php artisan ui bootstrap
Generate login / registration scaffolding...
php artisan ui bootstrap --auth
After installing the laravel/ui
Composer package and generating the frontend scaffolding, the package.json
file will include the Bootstrap and Webpixels CSS packages to help you get started prototyping your application's frontend without worrying about the design.
Before compiling CSS, open package.json
and replace devDependencies like in the example below. This way you'll make sure to use the latest Bootstrap version.
"devDependencies": {
"@popperjs/core": "^2.6.0",
"@webpixels/css": "^1.1.5",
"axios": "^0.21",
"bootstrap": "^5.1.1",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"postcss": "^8.2.2",
"sass": "^1.32.11",
"sass-loader": "^11.0.1"
}
Next, install your project's frontend dependencies using the Node package manager (NPM):
npm install
Configure Laravel Mix
Laravel Mix provides a clean, expressive API over compiling SASS, which are extensions of plain CSS that add variables, mixins, and other powerful features that make working with CSS much more enjoyable.
To configure Laravel Mix use the webpack.mix.js
file. It comes with a default initial setup, but we'll make some improvements here:
let mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css', {
sassOptions: {
includePaths: ['node_modules'],
},
});
mix.browserSync({
proxy: 'localhost:8000',
notify: true
});
if (mix.inProduction()) {
mix.version();
mix.sourceMaps();
}
To run your Mix tasks you only need to execute one of the NPM scripts that are included in the default Laravel package.json
file. When you run the dev
or production
scripts, all of your application's CSS and JavaScript assets will be compiled and placed in your application's public directory. We will use these later, but just to be aware, here are the commands:
// Run all Mix tasks...
npm run dev
// Run all Mix tasks and minify output...
npm run prod
The frontend scaffolding is now ready. In the following steps, we will set up the database connection and run the migrations which will allow us to create some beautiful authentication screens.
Add the CSS styles
As I previously mentioned, we'll use Webpixels CSS to style our dashboard. It is a utility and component-centric Design System based on Bootstrap for fast, responsive UI development.
Open the resource/sass/app.scss
file and replace its content with the following lines:
@import "@webpixels/css/base";
@import "@webpixels/css/forms";
@import "@webpixels/css/components";
@import "@webpixels/css/utilities";
Webpixels CSS will automatically load the latest version of Bootstrap 5. To learn more about how to customize it read the documentation.
Add Bootstrap to your app's JS
Open the resource/js/app.js
file and replace its content with the following lines:
require('bootstrap');
Since now we are using Bootstrap 5, you can remove the bootstrap.js
file located in the same folder. That one is added when laravel/ui
is installed to include jQuery and Popper, which is not the case anymore.
Great! On the front end side, we're all set. Next, we will focus on setting up the database and build our app's backend.
Database setup
When you create a new Laravel project, the installation process automatically creates a .env file for configuration and credentials. Depending on your setup, you’ll need to modify the following block of settings to match your database configuration:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
With your connection set up, let's run the migrations using the dedicated artisan command:
php artisan migrate
Let's build our app
These are the final steps before starting our app:
Generate the app's key
Since this is your first time starting this project, you must set the key configuration option in your config/app.php configuration file:
php artisan key:generate
Run database migrations
Before running the migrations, you may need to manually configure the default string length generated by migrations in order for MySQL to create indexes for them. You may configure the default string length by calling the Schema::defaultStringLength
method within the boot method of your App\Providers\AppServiceProvider
class:
use Illuminate\Support\Facades\Schema;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
}
To run all of your outstanding migrations, execute the migrate Artisan command:
php artisan migrate
Start the app
After the application has been created, you may start Laravel's local development server using the Artisan CLI's serve command:
php artisan serve
The database is all set so we will take care now of the user interface.
Set up the routes
By default, Laravel comes with a couple of implemented routes used for displaying the Home and Welcome pages of the website. It also creates the authentication routes automatically when you install Laravel UI.
Next, we'll make some minor changes for displaying the Home page. Copy this in your routes/web.php
file.
Auth::routes();
Route::get('/', [App\Http\Controllers\HomeController::class, 'index']);
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Create the layout
For creating the authentication pages and forms we will use Laravel's class-based components. Let's create a new guest layout using the make:component
command which will place it in the App\View\Components
directory:
php artisan make:component GuestLayout
The make:component
command will also create a view template for the component. The view will be placed in the resources/views/components
directory.
Once the layout component has been defined, place this content inside it:
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<main class="py-4">
{{ $slot }}
</main>
</div>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
</body>
</html>
As you can see, we are doing a few things here:
- added the Inter font
- linked the CSS styles
- added the
$slot
variable within our layout component used for injecting the content from other pages - linked the app's scripts
Now we are ready to build the authentication pages :)
Create the authentication screens
For this part, I will use one of the free and beautiful pre-made authentication screens from the UI components library provided by Webpixels. They're all crafted with the latest version of Bootstrap (currently v5) and you can use the HTML code to create all the pages you need.
In the following example, I took the HTML from Webpixels and replaced its elements with the ones needed by Laravel to become fully functional.
The login page
Copy and paste the code below in your login page located in the resources/views/auth/login.blade.php
:
<x-guest-layout>
<div class="px-5 py-5 p-lg-0 bg-surface-secondary">
<div class="d-flex justify-content-center">
<div class="col-lg-5 col-xl-4 p-12 p-xl-20 position-fixed start-0 top-0 h-screen overflow-y-hidden bg-primary d-none d-lg-flex flex-column">
<!-- Logo -->
<a class="d-block" href="#">
<img src="https://preview.webpixels.io/web/img/logos/clever-light.svg" class="h-10" alt="...">
</a>
<!-- Title -->
<div class="mt-32 mb-20">
<h1 class="ls-tight font-bolder display-6 text-white mb-5">
Let’s build something amazing today.
</h1>
<p class="text-white-80">
Maybe some text here will help me see it better. Oh God. Oke, let’s do it then.
</p>
</div>
<!-- Circle -->
<div class="w-56 h-56 bg-orange-500 rounded-circle position-absolute bottom-0 end-20 transform translate-y-1/3"></div>
</div>
<div class="col-12 col-md-9 col-lg-7 offset-lg-5 border-left-lg min-h-lg-screen d-flex flex-column justify-content-center py-lg-16 px-lg-20 position-relative">
<div class="row">
<div class="col-lg-10 col-md-9 col-xl-6 mx-auto ms-xl-0">
<div class="mt-10 mt-lg-5 mb-6 d-flex align-items-center d-lg-block">
<span class="d-inline-block d-lg-block h1 mb-lg-6 me-3">?</span>
<h1 class="ls-tight font-bolder h2">
{{ __('Nice to see you!') }}
</h1>
</div>
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="mb-5">
<label class="form-label" for="email">{{ __('E-Mail Address') }}</label>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<div class="mb-5">
<div class="d-flex align-items-center justify-content-between">
<div>
<label class="form-label" for="password">{{ __('Password') }}</label>
</div>
<div class="mb-2">
@if (Route::has('password.request'))
<a href="{{ route('password.request') }}" class="small text-muted">Forgot password?</a>
@endif
</div>
</div>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<div class="mb-5">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary w-full">
{{ __('Sign in') }}
</button>
</div>
</form>
<div class="my-6">
<small>{{ __('Don\'t have an account') }}</small>
<a href="{{ route('register') }}" class="text-warning text-sm font-semibold">{{ __('Sign up') }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</x-guest-layout>
The register page
Copy and paste the code below in your register page located in the resources/views/auth/register.blade.php
:
<x-guest-layout>
<div class="px-5 py-5 p-lg-0 bg-surface-secondary">
<div class="d-flex justify-content-center">
<div class="col-lg-5 col-xl-4 p-12 p-xl-20 position-fixed start-0 top-0 h-screen overflow-y-hidden bg-primary d-none d-lg-flex flex-column">
<!-- Logo -->
<a class="d-block" href="#">
<img src="https://preview.webpixels.io/web/img/logos/clever-light.svg" class="h-10" alt="...">
</a>
<!-- Title -->
<div class="mt-32 mb-20">
<h1 class="ls-tight font-bolder display-6 text-white mb-5">
Let’s build something amazing today.
</h1>
<p class="text-white-80">
Maybe some text here will help me see it better. Oh God. Oke, let’s do it then.
</p>
</div>
<!-- Circle -->
<div class="w-56 h-56 bg-orange-500 rounded-circle position-absolute bottom-0 end-20 transform translate-y-1/3"></div>
</div>
<div class="col-12 col-md-9 col-lg-7 offset-lg-5 border-left-lg min-h-lg-screen d-flex flex-column justify-content-center py-lg-16 px-lg-20 position-relative">
<div class="row">
<div class="col-lg-10 col-md-9 col-xl-6 mx-auto ms-xl-0">
<div class="mt-10 mt-lg-5 mb-6 d-flex align-items-center d-lg-block">
<span class="d-inline-block d-lg-block h1 mb-lg-6 me-3">?</span>
<h1 class="ls-tight font-bolder h2">
{{ __('Create an account') }}
</h1>
</div>
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="mb-5">
<label class="form-label" for="email">{{ __('Name') }}</label>
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<div class="mb-5">
<label class="form-label" for="email">{{ __('E-Mail Address') }}</label>
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<div class="mb-5">
<label class="form-label" for="password">{{ __('Password') }}</label>
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<div class="mb-5">
<label class="form-label" for="password-confirm">{{ __('Confirm Password') }}</label>
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
<div>
<button type="submit" class="btn btn-primary w-full">
{{ __('Sign up') }}
</button>
</div>
</form>
<div class="my-6">
<small>{{ __('Already have an account?') }}</small>
<a href="{{ route('login') }}" class="text-warning text-sm font-semibold">{{ __('Sign in') }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</x-guest-layout>
Other authentication screens
Inside your Laravel app, you will notice there are additional pages for account verification, password reset, and more. I took care of those too and you can find them in the project's repo.
For those who want to explore the code, here's the GitHub repo for this project.
Coming up
This is the first part of this series. In order to keep it clean and simple, I will dedicate one article for each major step towards our app completion.
In the following parts we will take it a step further and prepare your app for more complex pages and features, like:
- user list and management
- user roles and permissions
- simple blog system
Follow us and subscribe to the newsletter to be the first to find out when we release them.