5

I am not able to make work styles in my mails blade.

The final goal is to have a layout for mails. By now, for testing puposes, I have:

  • The controller that sends the mail
  • The mail component
  • The maileable class
  • The layout for emails, as a duplicated of guest.blade.php jetstream file
  • One specific email blade file, the content to add in the previus layout, is a duplicated of welcome.blade.php jetstream file

app\Http\Controllers\TestController.php

<?php
namespace App\Http\Controllers;

use App\Mail\TestingMail;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;

class TestController extends Controller
{
    public function __invoke(Request $request)
    {
        $result = Mail::to($request->user())
                ->send(new TestingMail());
        return view('welcome');
     }
}

app\View\Components\MailLayout.php

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class MailLayout extends Component
{
    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\Contracts\View\View|\Closure|string
     */
    public function render()
    {
        return view('layouts.mail');
    }
}

app\Mail\TestingMail.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TestingMail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('mails.general');
    }
}

resources\views\layouts\mail.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap">

        <!-- Scripts -->
        @vite(['resources/css/app.scss', 'resources/js/app.js'])
    </head>
    <body>
        <div class="font-sans text-gray-900 antialiased">
            {{ $slot }}
        </div>
    </body>
</html>

resources\views\mails\general.blade.php

<x-mail-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
                <x-jet-welcome />
            </div>
        </div>
    </div>
</x-mail-layout>

I receive the mail with no styles. The only help that I found was this link https://ralphjsmit.com/tailwind-css-multiple-configurations-laravel-mix but that was wrote for laravel mix, I don't have idea to migrate this webpack.mix.js to vite.

Any help or orientation will be apreciated, thanks.

EDIT I finnally made it work, combining the @Jaap's answer and other package: https://github.com/fedeisas/laravel-mail-css-inliner

https://github.com/motomedialab/laravel-vite-helper

vite helper is not ideal because it does not work when npm run dev is running, but it is sufficient for me.

4 Answers 4

6

I don't know if you still need this.

But I managed to get it to work nicely with some packages, a custom vite config and the --watch flag. Probably the best work around that I created for this.

This allows you to quickly build emails locally which almost works the same as dev

First I created a file called mail.scss in resources/css/ with these contents:

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

I only need the tailwind basics so I keep these for now.

Then I installed these packages

And inside the config from laravel-mail-css-inliner I will add my SCSS file like:

config/css-inliner.php

<?php
return [
    'css-files' => [
        public_path(vite('resources/css/mail.scss', 'build', true))
    ],

];

Now I created a new vite config for just my emails that's really basic:

vite-email.config.js

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
  plugins: [
    laravel({
      input: [
        'resources/css/mail.scss',
      ],
      refresh: true,
    }),
  ],
});

Now finally in my package.json I have added the following script:

{
    "scripts": {
        // Mail
        "build:mail": "vite build --config ./vite-email.config.js --watch",
        // General
        "dev": "vite",
        "build": "vite build",
        "build:server": "vite build --ssr",
        "build:all": "npm run build && npm run build:server"
    },
}

As you see in the top script build:mail I am using the vite-email.config.js I created. This will only build the mail.scss to the public/build folder.

Now I am also using the flag --watch. This enables rollup watcher and will watch all the files and trigger a rebuild.

So if I change mail/order-sent.blade.php and add some extra tailwindcss classes. It will rebuild again.

Because you're using a custom vite config, and you're only rebuilding your mail.scss and not app.js and app.scss for example, this will be really fast. It usually builds in roughly 100-500ms at me.

Sign up to request clarification or add additional context in comments.

7 Comments

Wow, this looks nice. I will test it as soon as possible. Thanks @Aspheleia !!
@msolla Did you end up testing this solution yet?
Using the public path results in file_get_contents errors.
@Oddman: were you able to solve the public path problem?
@YeasirArafatMajumder yes - you have to build the file, and then use the usual process vite path using the vite helper. And you can't have the dev process running for your CSS builds, else it all fucks up. It's extremely fickle. But once on production it's generally fine.
|
4

For emails you want your styles to be inlined. I don’t think that’s a functionality that Vite, Tailwind or Blade for that matter have out of the box.

There is a package, however, that seems to solve your problem: https://github.com/fedeisas/laravel-mail-css-inliner

Never tried that one before, but it seems quite active. However the practice of auto-inlining is quite common for emails so Google around for that one. You’ll probably find some package that suits your needs.

4 Comments

Works perfectly, thanks! Now I have other problem, in the package config file you must provide the path of css file in public dir, but vite generates public\build\assets\app.68478134.css file, with this random number. I can't find in vite docs how to get this path dinamically from php
finnally I used a vite helper from github, it is not ideally because in dev not loads the styles...but in prouduction works. I will update the question.
What is the vite helper? @msolla
have problem imppoortinng the hot viiite fille :(
2

other way around (thats what i did) ...

you can run make a minified build of your current tailwind :

npx tailwindcss -o build.css --minify

and then call for it with vite in your blade.php email layout file

@vite('YOURPATH/build.css')

optionally you can add the composer package to write inline css

Comments

0

You have to build the vite files (npm run build) first, then include them in your mail template. Or you can inline them in a big <style> block.

If you want a better example, take a look at how welcome.blade.php does this in a fresh Laravel install.

1 Comment

Yes, that is done. My resources\views\mails\general.blade.php file is extending resources\views\layouts\mail.blade.php and I include assets with vite there (@vite(['resources/css/app.scss', 'resources/js/app.js'])) I had run npm run build and npm run dev, but doesn't work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.