Introduction

Laravel Sail is a Docker development environment included by default in Laravel since version 8. It allows you to quickly get a PHP development environment up and running, tailored for running Laravel applications with built-in support for NPM / Node.

In this guide, you’ll bootstrap a new Laravel application with Laravel Sail and create a styled “about me” landing page using Tailwind CSS, a utility-first CSS framework designed for rapidly building custom user interfaces. At the end, you’ll have a base that you can use to further develop a Laravel application using Tailwind CSS for the front end and Sail for the development environment.

If you’d prefer to follow along from an existing codebase rather than creating the project from scratch, you can access the finished demo application code at do-community/laravel-tailwind-starter on GitHub.

Prerequisites

tailwind illustration for: Prerequisites

Although the code shared in this guide should work seamlessly across multiple environments and systems, the instructions explained here were tested within an Ubuntu 20.04 local system running Docker and Docker Compose. Regardless of your base operating system, here’s what you’ll need to set up in order to get started:

  • Docker Compose installed on your local machine. Docker Compose comes included by default with Docker Desktop for both Windows and MacOS systems, but Linux users need to install the Compose executable, following Step 1 of How To Install and Use Docker Compose on Ubuntu 20.04.
  • A code editor for PHP (optional). A code editor helps making code easier to read and to format, and can improve your productivity by pointing out issues before you execute your code. You can follow our guide on How To Set Up Visual Studio Code for PHP Projects to set up VSCode, a free code editor, within your local development environment.

Step 1 — Creating a New Laravel Application Using the Laravel Builder Script

To get started, you’ll download and execute the official Laravel builder script, which will pull in the necessary Docker container images to build your development environment and then bootstrap a new application in your current folder. This installation method doesn’t require you to have PHP installed on your system, requiring only that you download and execute the builder script that will set up the Docker environment where you can run the actual Laravel installer.

At the end, the script asks for your sudo password to make sure the application folder has correct permissions for your system user. You can access the script URL from your browser to verify its contents before running the next command. In this example, we’re using the name <^>myapp<^>, but you are free to replace this with a different name:

				
					
curl -s https://laravel.build/&lt;^&gt;myapp&lt;^&gt; | bash

				
			
				
					
[secondary_label Output]

Unable to find image 'laravelsail/php80-composer:latest' locally

latest: Pulling from laravelsail/php80-composer

852e50cd189d: Pull complete 

0266fc315b01: Pull complete 

…

Application ready! Build something amazing.

Sail scaffolding installed successfully.

 

Please provide your password so we can make some final adjustments to your application's permissions.

 

[sudo] password for sammy: 

 

Thank you! We hope you build something incredible. Dive in with: cd myapp &amp;&amp; ./vendor/bin/sail up

				
			

When the installation is finished, access the new application directory and get the Sail environment up with:

				
					
cd &lt;^&gt;myapp&lt;^&gt;

./vendor/bin/sail up

				
			

This will bring the environment up in foreground mode, so you can follow up with the logs from all running containers. You’ll see a few different services getting started, using different ports to communicate between each other:

				
					
[secondary_label Output]

...

mailhog_1 | [HTTP] Binding to address: 0.0.0.0:8025

...

laravel.test_1 | Starting Laravel development server: http://0.0.0.0:80

...

meilisearch_1 | Server listening on: "http://0.0.0.0:7700"

...

mysql_1 | 2021-06-23T01:15:24.327234Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.25' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.

...

selenium_1 | 01:14:57.417 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444

...

redis_1 | 1:M 23 Jun 2021 01:14:54.243 * Running mode=standalone, port=6379.

…

				
			

These are services configured by default within Sail environments. For more information about each of them, refer to the official Sail documentation.

Next, access the application from your browser at:

				
					
http://localhost

				
			

If all steps were successful, you’ll see a page like this:

The application is now bootstrapped.

You can now stop the Sail environment that is running on your terminal by typing CTRL+C.

Step 2 — Using Laravel Sail

Laravel Sail offers several shortcuts to manage your development environment. Most commands and arguments are based on the default Docker Compose API.

Controlling the Environment

To bring the environment up in background mode, you can run:

				
					
./vendor/bin/sail up -d

				
			

To stop a Sail environment that was previously initiated in background mode, run:

				
					
./vendor/bin/sail stop

				
			

This won’t delete attached networks or volumes.

To bring back an environment that was previously stopped with a sail stop command, you can use:

				
					
./vendor/bin/sail start

				
			

To stop an environment and also delete all associated resources such as volumes and networks, you can use the sail down command. Please notice that this command will delete any data that was previously created and is only available inside containers, such as records stored in a database.

				
					
./vendor/bin/sail down

				
			
				
					
[secondary_label Output]

Stopping laravel-tailwind-starter_laravel.test_1 ... done

Stopping laravel-tailwind-starter_redis_1        ... done

Stopping laravel-tailwind-starter_selenium_1     ... done

Stopping laravel-tailwind-starter_mysql_1        ... done

Stopping laravel-tailwind-starter_mailhog_1      ... done

Stopping laravel-tailwind-starter_meilisearch_1  ... done

Removing laravel-tailwind-starter_laravel.test_1 ... done

Removing laravel-tailwind-starter_redis_1        ... done

Removing laravel-tailwind-starter_selenium_1     ... done

Removing laravel-tailwind-starter_mysql_1        ... done

Removing laravel-tailwind-starter_mailhog_1      ... done

Removing laravel-tailwind-starter_meilisearch_1  ... done

Removing network laravel-tailwind-starter_sail

				
			

Checking Status and Logs

If your environment is down, bring it back up with:

				
					
./vendor/bin/sail up -d

				
			

When your environment is up and running, you can check the status of all active containers with:

				
					
./vendor/bin/sail ps

				
			
				
					
[secondary_label Output]

           Name                         Command                  State                              Ports                        

---------------------------------------------------------------------------------------------------------------------------------

myapp_laravel.test_1   start-container                  Up             0.0.0.0:80-&gt;80/tcp,:::80-&gt;80/tcp, 8000/tcp          

myapp_mailhog_1        MailHog                          Up             0.0.0.0:1025-&gt;1025/tcp,:::1025-&gt;1025/tcp,           

                                                                             0.0.0.0:8025-&gt;8025/tcp,:::8025-&gt;8025/tcp            

myapp_meilisearch_1    tini -- /bin/sh -c ./meili ...   Up (healthy)   0.0.0.0:7700-&gt;7700/tcp,:::7700-&gt;7700/tcp            

myapp_mysql_1          docker-entrypoint.sh mysqld      Up (healthy)   0.0.0.0:3306-&gt;3306/tcp,:::3306-&gt;3306/tcp, 33060/tcp 

myapp_redis_1          docker-entrypoint.sh redis ...   Up (healthy)   0.0.0.0:6379-&gt;6379/tcp,:::6379-&gt;6379/tcp            

myapp_selenium_1       /opt/bin/entry_point.sh          Up             4444/tcp                                       

 

				
			

The output from the sail ps command will tell you which containers related to that specific environment are currently active, which ports are being redirected, and more importantly, in which state each container is. In the previous example output, all services are up.

To check the containers logs when you’re running your environment in background mode, you can use:

				
					
./vendor/bin/sail logs

				
			

This will show you the latest logs from all services.

				
					
Attaching to laravel-tailwind-starter_laravel.test_1, laravel-tailwind-starter_mailhog_1, laravel-tailwind-starter_mysql_1, laravel-tailwind-starter_redis_1, laravel-tailwind-starter_selenium_1, laravel-tailwind-starter_meilisearch_1

...

mysql_1         | 2021-06-24T15:08:06.435530Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.25'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.

...

meilisearch_1   | [2021-06-24T15:16:38Z INFO  actix_web::middleware::logger] 127.0.0.1:60874 "GET /health HTTP/1.1" 200 22 "-" "Wget" 0.000056

...

laravel.test_1  | [Thu Jun 24 15:08:07 2021] PHP 8.0.7 Development Server (http://0.0.0.0:80) started

...

 

selenium_1      | 15:08:06.864 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444

...

redis_1         | 1:M 24 Jun 2021 15:08:05.280 * Ready to accept connections

...

mailhog_1       | 2021/06/24 15:08:05 Serving under http://0.0.0.0:8025/

				
			

You can also see logs per service by providing an additional argument to the command call:

				
					
./vendor/bin/sail logs redis

				
			
				
					
[secondary_label Output]

Attaching to laravel-tailwind-starter_redis_1

redis_1         | 1:C 24 Jun 2021 15:08:05.278 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

redis_1         | 1:C 24 Jun 2021 15:08:05.278 # Redis version=6.2.4, bits=64, commit=00000000, modified=0, pid=1, just started

...

redis_1         | 1:M 24 Jun 2021 15:08:05.280 * RDB memory usage when created 0.77 Mb

redis_1         | 1:M 24 Jun 2021 15:08:05.280 * DB loaded from disk: 0.000 seconds

redis_1         | 1:M 24 Jun 2021 15:08:05.280 * Ready to accept connections

				
			

Running Artisan and Composer

While working on your Laravel application, you’ll often be required to run artisan commands to build, test, and manage your application. You'll also need to run composer commands to manage your PHP dependencies. In addition to the default Docker Compose API, Sail offers helpful shortcuts to execute these commands in your application container (myapp_laravel.test_1 in the example output). With a regular Docker Compose setup, running Artisan would look like this:

				
					
[label With Docker Compose Only]

docker-compose exec app php artisan

				
			

With Sail, the equivalent call is shortened to:

				
					
[label Running Artisan With Sail]

./vendor/bin/sail artisan

				
			

You can run Composer in a similar way:

				
					
[label Running Composer With Sail]

./vendor/bin/sail composer

				
			

For more information about all features and commands available, please visit the official Laravel Sail Documentation.

You’re now familiar with how to manage your Sail development environment and how to run commands on the application container. In the next step, you’ll set up Tailwind CSS to design and style your landing page.

Step 3 — Setting Up Tailwind CSS with Laravel

Next, you’ll install and set up Tailwind CSS to build a landing page.

Make sure your Sail environment is up and running, then install Laravel’s front end dependencies with the npm command, which is used to download and manage JavaScript packages :

				
					
./vendor/bin/sail npm install

				
			
				
					
[secondary_label Output]

...

added 1327 packages, and audited 1328 packages in 20s

 

99 packages are looking for funding

  run `npm fund` for details

...

 

				
			

Then, install Tailwind and its dependencies with:

				
					
./vendor/bin/sail npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

				
			
				
					
[secondary_label Output]

...

added 9 packages, removed 22 packages, changed 7 packages, and audited 1315 packages in 5s

 

99 packages are looking for funding

  run `npm fund` for details

...

				
			

Next, you’ll need to create a configuration file for Tailwind. To do that, you’ll use npx, which stands for _Node package executer_ and allows you to execute a Node package. The following npx command will generate a new default Tailwind configuration for your application:

				
					
./vendor/bin/sail npx tailwindcss init

				
			

This will create a new configuration file named tailwind.config.js in your project’s root directory with the following content:

				
					
[label tailwind.config.js]

module.exports = {

  purge: [],

  darkMode: false, // or 'media' or 'class'

  theme: {

    extend: {},

  },

  variants: {

    extend: {},

  },

  plugins: [],

}

				
			

Although there are different ways to set up Tailwind within a project, in this guide we’ll configure Tailwind with Laravel Mix and webpack. Both libraries are used to compile and output front end resources.

Open the file webpack.mix.js using your code editor. It will look like this:

				
					
mix.js('resources/js/app.js', 'public/js')

    .postCss('resources/css/app.css', 'public/css', [

        //

    ]);

				
			

Laravel Mix uses PostCSS to compile CSS resources. Remove the // characters and include the following highlighted line, which will add Tailwind CSS in the list of CSS resources to process:

Remove the // characters and include the following highlighted line, which requires Tailwind as a PostCSS plugin:

				
					
[label webpack.mix.js]

  mix.js("resources/js/app.js", "public/js")

    .postCss("resources/css/app.css", "public/css", [

&lt;^&gt;     require("tailwindcss"),&lt;^&gt;

    ]);

				
			

Save the file after making this change.

Next, include Tailwind within the application’s main CSS file. Open resources/css/app.css in your code editor and add the following 3 lines to this file:

				
					
[label resources/css/app.css]

@tailwind base;

@tailwind components;

@tailwind utilities;

				
			

Save when finished.

Next, you’ll need to build the front end assets with NPM:

				
					
./vendor/bin/sail npm run dev

				
			

You will receive output that is similar to the following, with a line like the the highlighted Compiled Successfully portion that indicates you have integrated all of the components into your Sail environment:

				
					
[secondary_label Output]

                     	

   Laravel Mix v6.0.24   

                    	 

 

✔ &lt;^&gt;Compiled Successfully&lt;^&gt; in 5515ms

┌───────────────────────────────────────────────────────────────────┬──────────┐

│                                                          	File │ Size 	│

├───────────────────────────────────────────────────────────────────┼──────────┤

│                                                    	/js/app.js │ 597 KiB  │

│                                                   	css/app.css │ 3.81 MiB │

└───────────────────────────────────────────────────────────────────┴──────────┘

webpack compiled successfully

				
			

Tailwind is now configured and you have built the front end assets for your site.. In the next step, you’ll create a new landing page for your application.

Step 4 — Creating a Landing Page

With Tailwind’s configuration in place, you can now start building your application’s front end views. In Laravel, templates are typically stored in the resources/views directory. The page you saw before when opening the application from a browser (http://localhost) is defined in a single template called welcome.blade.php, in that directory.

In your code editor, open a new file called index.blade.php in the resources/views directory.

The following example template defines an “about me” HTML page with a few unstyled elements. It uses an example avatar image but you may replace it with an image of your own.

Create a new img directory in the public application folder:

				
					
mkdir public/img

				
			

Save your preferred image to this new directory, under the name profile_image.png.

In the following example, notice the use of the highlighted {{ asset… }} helper lines to define paths for both the CSS and the image files. This function outputs the correct public path for application resources located in the public directory.

Copy this content to your own index.blade.php:

				
					
[label resources/views/index.blade.php]

&lt;!doctype html&gt;

&lt;html lang="en"&gt;

&lt;head&gt;

    &lt;meta charset="utf-8"&gt;

    &lt;title&gt;Sammy the Shark - About Page&lt;/title&gt;

    &lt;meta name="description" content="My Application Description"&gt;

    &lt;meta name="author" content="Sammy"&gt;

    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;

    &lt;link href="&lt;^&gt;{{ asset('css/app.css') }}&lt;^&gt;" rel="stylesheet"&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;div&gt;

    &lt;img src="&lt;^&gt;{{ asset('img/profile_image.png') }}&lt;^&gt;" width="200" alt="avatar"/&gt;

    &lt;h1&gt;Sammy the Shark&lt;/h1&gt;

    &lt;p&gt;Content Creator&lt;/p&gt;

    &lt;p&gt;Hello, I'm Sammy. I am a friendly shark interested in Linux, coding, and community.

        You can find out more about me in the following links:&lt;/p&gt;

    &lt;div&gt;

        &lt;div&gt;&lt;a href="https://www.progressiverobot.com/"&gt;Twitter&lt;/a&gt;&lt;/div&gt;

        &lt;div&gt;&lt;a href="https://www.progressiverobot.com/"&gt;LinkedIn&lt;/a&gt;&lt;/div&gt;

        &lt;div&gt;&lt;a href="https://www.progressiverobot.com/"&gt;Instagram&lt;/a&gt;&lt;/div&gt;

    &lt;/div&gt;

&lt;/div&gt;

&lt;/body&gt;

&lt;/html&gt;

				
			

Save the file when you’re finished editing its contents.

Now edit the routes/web.php file to modify the main route so that it uses the newly created template. Open that file using your code editor, and replace welcome with index to change the view used by the main application endpoint. This is how the updated route declaration will look like once you’re finished:

				
					
[label routes/web.php]

Route::get('/', function () {

    return view('&lt;^&gt;index&lt;^&gt;');

});

				
			

Save the file. You can now reload the application page in your browser to see the new index page. You’ll see a page like this:

By default, Tailwind removes all styling from elements, which gives you freedom to build up your views by combining and mixing Tailwind CSS utility classes. In the next section, you’ll learn how to combine some of these utility classes in order to create a responsive “about me” page.

Step 5 — Styling Your Landing Page with Tailwind CSS

Building responsive pages is an important front end development requirement, since users may access your website or application from many different devices, each with different screen sizes.

Tailwind offers selectors that are able to apply styles per screen size. This way, you can create responsive containers by setting up the smallest width as default, and appending the additional responsive sizes for bigger screens. For instance, an element set with class="w-3/4 lg:w-1/2" will set a default width of 3 quarters the width of the parent element, which will be valid for smaller screens, but for larger screens (lg: selector), it will use half the width of the parent element.

Notice that you can combine the responsive selectors with any utility class, not only those associated with an element’s size. You can, for instance, hide an element at a certain breakpoint, change its colors, or even turn it into a grid with a variable number of columns.

You can find all available responsive selectors and their equivalent breaking points at the official Tailwind documentation.

The following template sets up a responsive content area at the center of the page, using background gradients and an example avatar image. For the buttons, it uses a grid flow system that will break the container into three columns starting at medium screens, but will make the buttons occupy the full container size when the page is accessed from smaller screens.

Open the resources/views/index.blade.php file that you created in the previous step in your code editor and replace the contents with the following template:

				
					
[label resources/views/index.blade.php]

&lt;!doctype html&gt;

&lt;html lang="en"&gt;

&lt;head&gt;

    &lt;meta charset="utf-8"&gt;

    &lt;title&gt;Sammy the Shark - About Page&lt;/title&gt;

    &lt;meta name="description" content="My Application Description"&gt;

    &lt;meta name="author" content="Sammy"&gt;

    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;

    &lt;link href="{{ asset('css/app.css') }}" rel="stylesheet"&gt;

&lt;/head&gt;

&lt;body class="bg-gradient-to-r from-blue-400 via-purple-600 to-blue-700"&gt;

 

&lt;div class="w-3/4 lg:w-1/2 mx-auto rounded-md bg-gray-200 shadow-lg m-20 p-10 text-center"&gt;

    &lt;img src="{{ asset('img/profile_image.png') }}" class="w-32 lg:w-1/6 bg-blue-600 mx-auto rounded-lg mb-4" alt="avatar"/&gt;

    &lt;h1 class="text-3xl"&gt;Sammy the Shark&lt;/h1&gt;

    &lt;p class="text-gray-500 pb-4"&gt;Content Creator&lt;/p&gt;

    &lt;p class="text-gray-700 mb-6"&gt;Hello, I'm Sammy. I am a friendly shark interested in Linux, coding, and community.

        You can find out more about me in the following links:&lt;/p&gt;

 

    &lt;div class="grid grid-cols-1 md:grid-cols-3 grid-flow-row gap-6"&gt;

        &lt;div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"&gt;&lt;a href="https://www.progressiverobot.com/"&gt;Twitter&lt;/a&gt;&lt;/div&gt;

        &lt;div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"&gt;&lt;a href="https://www.progressiverobot.com/"&gt;LinkedIn&lt;/a&gt;&lt;/div&gt;

        &lt;div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"&gt;&lt;a href="https://www.progressiverobot.com/"&gt;Instagram&lt;/a&gt;&lt;/div&gt;

    &lt;/div&gt;

 

&lt;/div&gt;

&lt;/body&gt;

&lt;/html&gt;

				
			

This template will produce the following page:

And this is how the page adapts to different screen sizes:

In the updated template, each HTML element has a number of Tailwind classes and selectors applied to them. Tailwind uses specific nomenclature for its utility classes to create responsive layouts. Each of the following is used in the example template to create the final result:

  • bg-gradient-to-r: creates a gradient from left to right, using the specified colors.
  • w-1/3: sets the width of the element to one third (1/3) of the parent’s element width. There are many different ways to set up an element’s width within Tailwind.
  • mx-auto: centers the element.
  • mr-* and other m variants: used to set up an element’s margins.
  • pb-* and other p variants: used to set up an element’s padding.

Check Tailwind’s official documentation for a complete reference of all available utility classes.

Conclusion

In this tutorial, you’ve bootstrapped a new Laravel application using Laravel Sail and Tailwind CSS. You also created a responsive “about me” landing page using Tailwind’s powerful utility classes.

If you’d like to build a more complex landing page and learn more about Tailwind in the process, you can follow our guide on How To Build a Styled Landing Page with Tailwind CSS for detailed instructions on how to create a complete website page with this framework.

If you’d like to learn more about Laravel in a project-based guide, you can refer to our How To Build a Links Landing Page in PHP with Laravel series. For more PHP content, check our PHP tag.