Build modern authentication screens with Laravel 8 and Bootstrap 5
Tags: bootstrap, laravel, dashboard, tutorial
Published: 2021-05-23 15:58:03

![Authentication Screens - Webpixels Components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r59gnerf9w22lvd4ehow.png)

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](https://github.com/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](https://laravel.com/docs/8.x/installation)

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.

```txt
composer require laravel/ui
```

Generate basic scaffolding...

```txt
php artisan ui bootstrap
```

Generate login / registration scaffolding...

```txt
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.

```json
"devDependencies": {
   "@popperjs/core": "^2.6.0",
   "@webpixels/css": "^1.0.1",
   "axios": "^0.21",
   "bootstrap": "^5.0.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)](https://www.npmjs.com/):

```txt 
npm install
```

## Configure Laravel Mix

[Laravel Mix](https://laravel.com/docs/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:

```js
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:

```txt
// 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](https://github.com/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: 

```scss
@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](https://webpixels.io/docs/css/).

## Add Bootstrap to your app's JS

Open the `resource/js/app.js` file and replace its content with the following lines: 

```js
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:

```txt
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:

```txt 
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:

```txt
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:

```php
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:

```txt 
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:

```txt
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. 

```php
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:

```txt
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:

```html
<!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](https://webpixels.io/components/application/application-pages/auth-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`:

```html
<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`:

```html
<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](https://github.com/webpixels/laravel-starter-kit).

For those who want to explore the code, here's the [GitHub](https://github.com/webpixels/laravel-starter-kit) 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 me and subscribe to the newsletter to be the first to find out when I release them.