Pretty Laravel x Tailwind emails

Jamie - Dec 14 '23 - - Dev Community

Hello again. This tutorial will guide you through creating visually appealing emails for Laravel applications using Tailwind. This work is based on the insightful post by Ralph J. Smit (https://ralphjsmit.com/laravel-emails-tailwind-css), which has been immensely helpful. In this post, I aim to provide a comprehensive guide on how to use Tailwind with Laravel emails. I'll address two challenges that I encountered: using Vite instead of Webpack, and working with non-SMTP transports, such as Resend.

The process includes the following steps:

  • Create the necessary Tailwind and Vite configurations
  • Build our email views and classes
  • Use an updated CSS inliner that is compatible with all transports

Tailwind Configurations

First, create a separate Tailwind configuration file. For instance, you can name it mailwind.css. Place this file in the root of your Laravel repository.

const defaultTheme = require("tailwindcss/defaultTheme");

module.exports = {
    content: ["./resources/views/emails/**/*.blade.php"],
    theme: {
        screens: {
            xxs: "375px",
            xs: "475px",
            ...defaultTheme.screens,
        },
        fontFamily: {
            sans: ['"DM Sans"', "system-ui"],
            filament: ["DM Sans", ...defaultTheme.fontFamily.sans],
            serif: ["Georgia", "ui-serif"],
            display: ['"PP Eiko"', "system-ui"],
            mono: ["JetBrains Mono", "monospace"],
        }
    },
};
Enter fullscreen mode Exit fullscreen mode

Next, create a new CSS file for our emails. Place it at resources/css/mail.css:

@config "../../mailwind.config.js";

@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Two important points to note: Firstly, we're using the @config directive to direct Tailwind to our new configuration. Secondly, we're only using the utilities to generate our utility classes. We don't need the base and components typically found in a Tailwind CSS file, as the inliner we'll use later can struggle to handle them.

Finally, update your vite.config.js to inform it about our new CSS file:

import { defineConfig } from "vite";
import laravel, { refreshPaths } from "laravel-vite-plugin";

export default defineConfig({
    plugins: [
        laravel({
            input: [
                "resources/css/mail.css",
                "resources/css/app.css",
                "resources/js/app.js",
            ],
            refresh: [...refreshPaths, "app/Livewire/**"],
        }),
    ],
});
Enter fullscreen mode Exit fullscreen mode

Laravel Email

With Artisan, we can create a new email and assign its content to a new view that we'll create.

php artisan make:mail MyEmail
Enter fullscreen mode Exit fullscreen mode
// app/Mail/MyEmail.php

public function content(): Content
{
    return new Content(
        view: 'emails.my-email',
    );
}
Enter fullscreen mode Exit fullscreen mode

Create the view in resources/views/emails/my-email.blade.php. You're welcome to use regular Blade features here, including layouts which are particularly useful when handling multiple emails.

@php
use Illuminate\Support\Facades\Vite;
@endphp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <style>
        {!! Vite::content('resources/css/mail.css') !!}
    </style>

    @if($subject = $attributes->get('subject'))
    <title>{{ $subject }}</title>
    @endif
</head>

<body>
    <div class="w-full h-full p-4 m-4 border">
            MY EMAIL GOES HERE
    </div>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

The {!! Vite::content('resources/css/mail.css') !!} is used for outputting our compiled CSS into the style tag that will be inlined next.

Inliner

Due to the varied behavior of email clients compared to web browsers, we need to inline styles. For instance, w-full should be replaced with width: 100% for each element that uses it. Don't fret, it's simpler than it sounds with the help of laravel-mail-css-inliner. However, as of writing, this doesn't work with every transport. For instance, it wouldn't work in production when used with Resend. But there's no need to worry, you can use my fork of the project which resolves this issue.

Begin by adding the following to your composer.json file:

"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/jamiedavenport/laravel-mail-css-inliner.git"
    }
]
Enter fullscreen mode Exit fullscreen mode

To download and install the package, run the command composer require "fedeisas/laravel-mail-css-inliner @dev".

Conclusion

That's all for now, but not quite everything. You still need to set up and send your emails. For those steps, I suggest referring to the Laravel documentation. Once you've done that, your emails will be beautifully formatted with Tailwind.

Follow up reading:

Until next time ✌️

. . . . . . . . . . . . . . . .