Blog / Créer des emails avec Vue et Tailwind en utilisant Inertia Mailable

Image used for article Créer des emails avec Vue et Tailwind en utilisant Inertia Mailable

Créer des emails avec Vue et Tailwind en utilisant Inertia Mailable




TL;DR: Comment construire facilement des modèles d'emails dynamiques tout en conservant ses outils Vue et Tailwindcss grâce au package Inertia Mailable.




Vous trouverez un projet Laravel d’exemple sur ce Github Repository. Découvrez-en plus sur CapsulesX ou Bluesky.




Inertia Mailable est un package qui vous permet de concevoir de fabuleux emails dans un projet Laravel en tirant parti de la puissance d’InertiaJS. Créer des designs d’emails interactifs et réactifs en construisant des composants VueJS, en les enrichissant avec TailwindCSS et en les intégrant dans vos mailables.




Ce package est en phase alpha et en développement continu. Il a été créé dans le but de simplifier la mise en page des emails tout en conservant le même procédé que pour le reste de la plateforme web. Un template d'email pourrait ainsi correspondre à un template de page.




Cet article propose un exemple d'utilisation du package Inertia Mailable. Il suit le contexte d'un utilisateur qui s'inscrit sur la plateforme, et, à qui un email de bienvenue doit être envoyé. En partant d'un projet Laravel VILT, un Mailable sera configuré avec son template d'email Markdown par défaut. Il sera alors transféré vers un composant VueJS en y ajoutant des classes TailwindCSS. Les étapes sont les suivantes :




Dans un premier temps, il est nécessaire de créer le fichier Mailable. Simplement en utilisant la commande Artisan : php artisan make:mail . En y ajoutant l’option markdown pour générer le visuel simultanément.



php artisan make:mail WelcomeNewUser --markdown=mail.welcome




Le Mailable correspondrait alors à ceci :



app/Mail/WelcomeNewUser.php


<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class WelcomeNewUser extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
    }

    public function envelope() : Envelope
    {
        return new Envelope( subject : 'Welcome New User' );
    }

    public function content() : Content
    {
        return new Content( markdown : 'mail.welcome' );
    }

    public function attachments() : array
    {
        return [];
    }
}




La partie envelope concerne le sujet, le destinataire et l'expéditeur de l'email. La partie content correspond au corps du message, qui est directement dirigé vers un fichier Blade au format Markdown situé dans le dossier resources/views/mail.



resources/views/mail/welcome.blade.php


<x-mail::message>
# Introduction

The body of your message.

<x-mail::button :url="''">
Button Text
</x-mail::button>

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>




Ce fichier Blade contient deux composants Blade x-mail::message et x-mail::button . Ces composants font partie intégrante du framework Laravel. Voici un exemple de ce à quoi ressemble le composant blade x-mail::button :



vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/button/blade.php


@props([
    'url',
    'color' => 'primary',
    'align' => 'center',
])
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>




Pour visualiser rapidement le template d’e-mail, il est nécessaire de créer une route.



routes/web.php


<?php

use Illuminate\Support\Facades\Route;
use App\Mail\WelcomeNewUser;

Route::get( '/', fn() => ( new WelcomeNewUser() )->render() );







La personnalisation de ce template étant limitée, il est nécessaire de publier les composants pour accéder à plus d'options. Une série de fichiers apparaîtra alors dans le dossier resources/views/vendor/mail.



php artisan vendor:publish --tag=laravel-mail




Les composants Blade apparaîtront dans le dossier html, tandis que le fichier default.css se trouvera dans le dossier themes.

Il est maintenant temps d’installer le package Inertia Mailable.



composer require capsulescodes/inertia-mailable 




L’étape suivante consiste à publier le fichier javascript ou typescript qui gérera les composants Vue via InertiaJS.



// Javascript

php artisan vendor:publish --tag=inertia-mailable-vue-js

// Typescript

php artisan vendor:publish --tag=inertia-mailable-vue-ts


INFO  Publishing [inertia-mailable-vue-js] assets.

  Copying file [/vendor/inertia-mailable/stubs/js/vue/mail.js] to [resources/js/mail.js] ........ DONE
  Copying file [/vendor/inertia-mailable/stubs/js/vue/mails/Welcome.vue] to [resources/js/mails/Welcome.vue]  DONE



Il faut ensuite ajouter le fichier javascript ou typescript nouvellement créé dans le fichier vite.config.js sous la catégorie ssr. Comme ces composants Vue sont générés côté serveur, ils doivent être compilés et stockés dans un dossier inaccessible aux visiteurs.



vite.config.js


import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';

export default defineConfig( {
    plugins : [
        laravel( {
            input : [ 'resources/css/app.css', 'resources/js/app.js' ],
            ssr : [ 'resources/js/mail.js' ],
            refresh : true,
        }),
        vue(),
    ],
    resolve : { alias : { '~': '/resources/js' } }
});




Dans le cadre de cet article un nouveau script ssr est ajouté au fichier package.json. Ce script permet de transpiler les fichiers nécessaires et de les stocker dans le dossier bootstrap/ssr. Ce script aura une importance particulière plus tard dans cet article.



package.json


{
    "type": "module",
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "ssr": "vite build --ssr"
    },
    "devDependencies": {
        "autoprefixer": "^10.4.20",
        "laravel-vite-plugin": "^1.0",
        "postcss": "^8.4.41",
        "tailwindcss": "^3.4.10",
        "vite": "^5.0"
    },
    "dependencies": {
        "@inertiajs/vue3": "^1.2.0",
        "@vitejs/plugin-vue": "^5.1.2",
        "vue": "^3.4.38"
    }
}



Et de modifier le Mailable précédemment créé : WelcomeNewUser .



app/Mail/WelcomeNewUser.php


<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
- use Illuminate\Mail\Mailable;
+ use CapsulesCodes\InertiaMailable\Mail\Mailable;
- use Illuminate\Mail\Mailables\Content;
+ use CapsulesCodes\InertiaMailable\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class WelcomeNewUser extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
    }

    public function envelope() : Envelope
    {
        return new Envelope( subject : 'Welcome New User' );
    }

    public function content() : Content
    {
-       return new Content( markdown : 'mail.welcome' );
+       return new Content( view : 'Welcome', props : [ 'name' => 'Capsules Codes' ] );
    }

    public function attachments() : array
    {
        return [];
    }
}
 
  • Pour injecter les données dans le composant Vue, on utilise désormais le mot clé props contrairement à with, qui est utilisé pour l’injection de données dans les layouts Blade.




Il est temps de visualiser le rendu. Lancer préalablement npm run ssr .




Si une erreur de ce type Error: proc_open(): posix_spawn() failed: No such file or directory apparait, cela indique que Node est introuvable , il faudra alors spécifier son chemin dans une variable .env : NODE_PATH.



> ssr
> vite build --ssr

vite v5.4.2 building SSR bundle for production...
✓ 10 modules transformed.
bootstrap/ssr/ssr-manifest.json   0.81 kB
bootstrap/ssr/mail.js            13.65 kB
✓ built in 112ms







🎉




Il ne reste plus qu’à personnaliser son e-mail depuis le composant Vue.



resources/js/mails/Welcome.vue


<script setup>

import Layout from '/vendor/capsulescodes/inertia-mailable/components/vue/Layout.vue';
import Table from '/vendor/capsulescodes/inertia-mailable/components/vue/tags/Table.vue';

const props = defineProps( { name : { type : String, required : true } } );

const app = import.meta.env.VITE_APP_NAME;
const image = 'https://raw.githubusercontent.com/capsulescodes/inertia-mailable/main/art/capsules-inertia-mailable-mail-image.png';

</script>

<template>

    <Layout>

        <template v-slot:default>

            <p>Hello, {{ props.name }}!</p>

            <p>This is a mail made with Laravel, Inertia, Vue</p>

            <Table align="center">

                <Table align="center">

                    <Table>

                        <img class="my-4 max-w-full" v-bind:src="image">

                    </Table>

                </Table>

            </Table>

            <p>Regards,</p>

            <p>{{ app }}</p>

        </template>

        <template v-slot:subcopy>

            <p class="text-xs">This is a subcopy made with Laravel, Inertia and Vue</p>

        </template>

    </Layout>

</template>
  • Pour plus de clarté, l’ajout d’un alias dans le fichier vite.config.js permet d’éviter d’importer les composants à partir de leur chemin absolu. /vendor/capsulescodes/inertia-mailable/components/vue deviendrait alors @/vue.




Et, cerise sur le gateau, il est possible d’éviter de transpiler les fichiers à chaque modification en utilisant l’option --watch. Cependant, il faudra rafraîchir le navigateur pour visualiser les changements.



"ssr": "vite build --ssr --watch"




Pour envoyer l’email, on ajoute une route send tout en configurant les variables .env qui concerne le Mailer.



routes/web.php


use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeNewUser;


Route::get( '/send', function(){ Mail::to( 'example@example.com' )->send( new WelcomeNewUser() ); } );



.env


MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"








Ravi d’avoir pu aider !

v1.4.0

Icône XIcône Github