Blog / Créer des emails avec Vue et Tailwind en utilisant Inertia Mailable
6 min - 11 Sep 2024
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 Capsules, X 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 [];
}
}
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>
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 !