Blog / Créer des emails avec React et Tailwind en utilisant Inertia Mailable
6 min - 04 Mar 2025
TL;DR: Comment construire facilement des modèles d'emails dynamiques tout en conservant ses outils React 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 React, en les enrichissant avec TailwindCSS et en les intégrant dans vos mailables.
Ce package est 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 RILT, un Mailable
sera configuré avec son template d'email Markdown
par défaut. Il sera alors transféré vers un composant React 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
.
C’est maintenant au tour du package Inertia Mailable.
composer require capsulescodes/inertia-mailable
L’étape suivante consiste à publier les fichiers javascript
ou typescript
qui géreront les composants React via InertiaJS.
// Javascript
php artisan vendor:publish --tag=inertia-mailable-react-js
// Typescript
php artisan vendor:publish --tag=inertia-mailable-react-ts
INFO Publishing [inertia-mailable-react-js] assets. Copying file [/vendor/inertia-mailable/stubs/js/react/mail.jsx] to [resources/js/mail.jsx] ............................ DONE Copying file [/vendor/inertia-mailable/stubs/js/react/mails/Welcome.jsx] to [resources/js/mails/Welcome.jsx] .......... DONE
Deux fichiers sont publiés, mail.jsx
: le fichier d'initialisation Inertia et Welcome.jsx
: le composant du mail.
Il faut ensuite ajouter le fichier Inertia nouvellement créé dans le fichier vite.config.js
sous la catégorie ssr
. Comme ces composants React sont générés côté serveur, ils peuvent ê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 tailwind from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
export default defineConfig( {
plugins : [
laravel( {
input : [ 'resources/css/app.css', 'resources/js/app.jsx' ],
ssr : [ 'resources/js/mail.jsx' ],
refresh : true,
}),
tailwind(),
react(),
],
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 utilité particulière plus tard dans cet article.
package.json
{
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"ssr": "vite build --ssr"
},
"dependencies" : {
"@inertiajs/react" : "^2.0.4",
"@tailwindcss/vite" : "^4.0.9",
"@vitejs/plugin-react" : "^4.3.4",
"concurrently" : "^9.0.1",
"laravel-vite-plugin" : "^1.2.0",
"react" : "^19.0.0",
"react-dom" : "^19.0.0",
"tailwindcss" : "^4.0.9",
"vite" : "^6.0.11"
}
}
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 React.
resources/js/mails/Welcome.jsx
import { Layout, Subcopy } from '/vendor/capsulescodes/inertia-mailable/components/js/react/Layout';
import Table from '/vendor/capsulescodes/inertia-mailable/components/js/react/tags/Table';
function Welcome( { name } )
{
const app = process.env.VITE_APP_NAME;
const image = '<https://raw.githubusercontent.com/capsulescodes/inertia-mailable/main/art/capsules-inertia-mailable-mail-image.png>';
return (
<Layout>
<div>
<p className="text-slate-800">Hello, { name }!</p>
<p className="pt-4 text-sm text-slate-600">This is a mail made with Laravel, Inertia and React</p>
<Table align="center">
<Table align="center">
<Table>
<img className="my-4 max-w-full" src={ image } />
</Table>
</Table>
</Table>
<p className="pb-4 text-sm text-slate-600">Regards,</p>
<p className="text-slate-800">{ app }</p>
</div>
<Subcopy>
<p className="text-xs text-slate-600">This is a subcopy made with Laravel, Inertia and React</p>
</Subcopy>
</Layout>
);
};
export default Welcome;
vite.config.js
permet d’éviter d’importer les composants à partir de leur chemin absolu. /vendor/capsulescodes/inertia-mailable/components/react
deviendrait alors @/react
.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 !