Pretty Laravel x Tailwind emails
Tags: laravel, tailwindcss, webdev, tutorial
Published: 2023-12-14 15:25:56


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.

```jsx
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"],
        }
    },
};
```

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

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

@tailwind utilities;
```

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:

```jsx
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/**"],
        }),
    ],
});
```

# Laravel Email

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

```bash
php artisan make:mail MyEmail
```

```php
// app/Mail/MyEmail.php

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

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.

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

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

```json
"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/jamiedavenport/laravel-mail-css-inliner.git"
    }
]
```

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:

- https://ralphjsmit.com/laravel-emails-tailwind-css
- [https://laravel.com/docs/10.x/mail](https://laravel.com/docs/10.x/mail#main-content)

Until next time ✌️