PHPackages                             visualbuilder/email-templates - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. visualbuilder/email-templates

ActiveLibrary

visualbuilder/email-templates
=============================

Email Template editor for Filament

4.0.13(1mo ago)11040.7k—3.6%40GPL-2.0-or-laterPHPPHP ^8.2CI passing

Since Jul 8Pushed 1mo ago3 watchersCompare

[ Source](https://github.com/visualbuilder/email-templates)[ Packagist](https://packagist.org/packages/visualbuilder/email-templates)[ Docs](https://github.com/visualbuilder/email-templates)[ RSS](/packages/visualbuilder-email-templates/feed)WikiDiscussions 5.x Synced 1mo ago

READMEChangelog (8)Dependencies (26)Versions (122)Used By (0)

Email Template Editor for Filament
==================================

[](#email-template-editor-for-filament)

[![Latest Version on Packagist](https://camo.githubusercontent.com/92a478d3e8c4a624bbc241427271b0f20281afa294f106c9c82bbc6e08f71ade/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f76697375616c6275696c6465722f656d61696c2d74656d706c617465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/visualbuilder/email-templates)[![Packagist Downloads](https://camo.githubusercontent.com/78dca683ec2b8974cb8f39c4f1a4bf38fde88840bc315e66e1da90aea5a80e5f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f76697375616c6275696c6465722f656d61696c2d74656d706c61746573)](https://camo.githubusercontent.com/78dca683ec2b8974cb8f39c4f1a4bf38fde88840bc315e66e1da90aea5a80e5f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f76697375616c6275696c6465722f656d61696c2d74656d706c61746573)[![run-tests](https://github.com/visualbuilder/email-templates/actions/workflows/run-tests.yml/badge.svg)](https://github.com/visualbuilder/email-templates/actions/workflows/run-tests.yml)[![GitHub last commit](https://camo.githubusercontent.com/2d8ae4ceca2c3a640fad1510047a88e864bcabe6e48f7c4c75d000e040bd35dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f76697375616c6275696c6465722f656d61696c2d74656d706c61746573)](https://camo.githubusercontent.com/2d8ae4ceca2c3a640fad1510047a88e864bcabe6e48f7c4c75d000e040bd35dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f76697375616c6275696c6465722f656d61696c2d74656d706c61746573)

[![Email Preview](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/social-card.jpg)](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/social-card.jpg)

### Why businesses and applications should use Email Templates

[](#why-businesses-and-applications-should-use-email-templates)

- **Time-saving**: Email templates eliminate the need to create emails from scratch, saving valuable time and effort.
- **Customisability**: Quick editing capabilities enable employees to personalise the content of the templates while maintaining a professional appearance.
- **Consistent branding**: Templates ensure that all emails adhere to the brand's guidelines, reinforcing brand recognition and professionalism.
- **Professional appearance**: Well-designed templates provide a polished and consistent look, enhancing the business's credibility and reputation.
- **Streamlined communication**: Prompt and efficient communication.
- **Flexibility**: Templates can be adapted for various purposes, such as promotional emails, customer support responses, newsletters, and more.
- **Easy updates**: Templates can be easily modified to reflect changes in offers, policies, or design elements, ensuring that communication remains current and aligned with business objectives.
- **Standardisation**: Templates enforce a standardized structure and format for emails, reducing errors and improving clarity in communication.
- **Scalability**: Email templates facilitate consistent messaging even as the business grows, ensuring a cohesive customer experience across all interactions.
- **Improved productivity**: With quick access to templates, employees can focus more on core tasks, increasing overall productivity within the business.

### This package provides:-

[](#this-package-provides-)

- Content management for email templates allowing authorised users to edit email template content in the admin.
- Templates can include model attribute tokens or config values which will be replaced, eg ##user.name## or ##config.app.name##
- Templates can be saved with different locales for multi-lingual capability.
- A generic method for quickly creating mail classes to speed up adding new templates and faster automation possiblities.
- Theme editor - Set your own colours and apply to specific templates.

We use the standard Laravel mail sending capability, the package simply allows content editing and faster adding of new template Classes

### Theme Editor

[](#theme-editor)

[![Email Preview](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/ThemeEditor.jpg)](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/ThemeEditor.jpg)

### HTML Email Template Editor

[](#html-email-template-editor)

Edit email content in the admin and use tokens to inject model or config content.

[![Email Preview](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/EmailEditor.png)](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/EmailEditor.png)

Version Compatibility
---------------------

[](#version-compatibility)

Package VersionFilamentLaravelPHP5.x5.x11.x, 12.x8.2+4.x4.x10.x, 11.x8.2+3.x3.x10.x, 11.x8.1+Multitenancy
------------

[](#multitenancy)

This package supports Filament's built-in multi-tenancy. When enabled, tenants can customise their own email templates while falling back to global system templates.

### Enabling Multitenancy

[](#enabling-multitenancy)

#### Via Config

[](#via-config)

In `config/filament-email-templates.php`:

```
'multitenancy' => [
    'enabled' => true,
    'tenant_model' => App\Models\Team::class,
    // Optional - derived automatically from tenant model if not set:
    // 'tenant_foreign_key' => 'team_id',
    // 'ownership_relationship' => 'team',
],
```

#### Via Plugin (Panel Provider)

[](#via-plugin-panel-provider)

```
use Visualbuilder\EmailTemplates\EmailTemplatesPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            EmailTemplatesPlugin::make()
                ->multitenancy()
                ->tenantModel(Team::class),
        ]);
}
```

### Running the Migration

[](#running-the-migration)

After enabling multitenancy, publish and run the migration to add the tenant column:

```
php artisan migrate
```

The migration adds a nullable tenant foreign key to both the `vb_email_templates` and `vb_email_templates_themes` tables.

### How It Works

[](#how-it-works)

**Template Fallback Chain**

When retrieving a template (e.g. for sending an email), the package checks:

1. **Tenant-specific template** — a template with the matching key AND the current tenant's ID
2. **Global template** — a template with the matching key and a NULL tenant ID

This means tenants only need to create templates they want to customise. All other emails automatically use the global system templates.

**In Filament Panels**

Tenant users see both their own templates and global system templates in the resource list. When a tenant creates a new template, it is automatically assigned to their tenant.

**Sending Emails**

Pass the tenant ID explicitly when outside a Filament context (e.g. in queue workers or artisan commands):

```
$template = EmailTemplate::findEmailByKey('user-welcome', 'en_GB', $tenantId);
```

Within a Filament panel, the tenant is resolved automatically from `Filament::getTenant()`.

### Cache Behaviour

[](#cache-behaviour)

Cache keys include the tenant context to prevent cross-tenant cache pollution. When a template is updated or deleted, both the tenant-specific and global cache entries are cleared to ensure the fallback chain stays consistent.

After enabling multitenancy on an existing installation, run `php artisan cache:clear` to reset stale cache keys.

Installation
------------

[](#installation)

Get the package via composer:

```
# For Filament 5.x
composer require visualbuilder/email-templates:^5.0

# For Filament 4.x
composer require visualbuilder/email-templates:^4.0
```

Running the install command will copy the template views, migrations, seeders and config file to your app.

The --seed option will populate 7 default templates which you can then edit in the admin panel.

```
 php artisan filament-email-templates:install --seed
```

Note: The seeder can also be edited directly if you wish to prepopulate with your own content. `database\Seeders\EmailTemplateSeeder.php`

### Adding the plugin to a panel

[](#adding-the-plugin-to-a-panel)

Add this plugin to panel using plugins() method in app/Providers/Filament/AdminPanelProvider.php:

```
use Visualbuilder\EmailTemplates\EmailTemplatesPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            EmailTemplatesPlugin::make(),
            // ...
        ]);
}
```

Menu Group and sort order can be set in the config

### Enabling navigation

[](#enabling-navigation)

In the config file `config/filament-email-templates.php` navigation can be disabled/enabled

```
use Filament\Pages\Enums\SubNavigationPosition;

    /**
     * Admin panel navigation options
     */
    'navigation' => [
        'enabled' => true,
        'templates' => [
            'sort' => 10,
            'label' => 'Email Templates',
            'icon' => 'heroicon-o-envelope',
            'group' => 'Content',
            'cluster' => false,
            'position' => SubNavigationPosition::Top
        ],
        'themes' => [
            'sort' => 20,
            'label' => 'Email Template Themes',
            'icon' => 'heroicon-o-paint-brush',
            'group' => 'Content',
            'cluster' => false,
            'position' => SubNavigationPosition::Top
        ],
    ],
```

Or you can use a closure to enable navigation only for specific users:

```
// AdminPanelProvider.php
    ->plugins([
// ...
        EmailTemplatesPlugin::make()
                ->enableNavigation(
                    fn () => auth()->user()->can('view_email_templates') || auth()->user()->can('view_any_email_templates'),
               ),
    ])
```

### Theme Screenshots

[](#theme-screenshots)

The package supports optional screenshot capture for email template themes. When configured, a "Capture" button appears on the theme list page that generates a preview image of how emails look with that theme's colours. Screenshots are stored via Spatie MediaLibrary on the `EmailTemplateTheme` model.

#### Configuring Screenshot Capture

[](#configuring-screenshot-capture)

Provide a `screenshotCapture` closure on the plugin. The closure receives the rendered email HTML string and should return `['image' => binary, 'contentType' => 'image/png']` or `null`.

```
// AdminPanelProvider.php
->plugins([
    EmailTemplatesPlugin::make()
        ->screenshotCapture(function (string $html): ?array {
            // Example using a screenshot service (Browsershot, Puppeteer Lambda, etc.)
            return app(ScreenshotService::class)->htmlToBase64($html, [
                'viewport' => 'mobile',
                'fullPage' => true,
            ]);
        }),
])
```

When configured, the theme list page will show:

- A **preview thumbnail** column showing the captured screenshot
- A **Capture** button on each row to capture/recapture a single theme
- A **Capture Screenshots** bulk action to capture multiple themes at once
- A **manual upload** field in the theme edit form

When `screenshotCapture` is not configured, these features are hidden and the package works exactly as before.

#### Screenshot Storage

[](#screenshot-storage)

Screenshots are stored as a `screenshot` media collection (single file) on the `EmailTemplateTheme` model. A `thumb` conversion (400x600, contain) is generated automatically for the list view.

The model implements `Spatie\MediaLibrary\HasMedia`, so you can access screenshots programmatically:

```
$theme->getFirstMediaUrl('screenshot');           // Original
$theme->getFirstMediaUrl('screenshot', 'thumb');  // Thumbnail
```

Usage
-----

[](#usage)

### Tokens

[](#tokens)

Token format is ##model.attribute##. When calling the email pass any referenced models to replace the tokens automatically.

You can also include config values in the format ##config.file.key## eg ##config.app.name##.

*In the email templates config file you must specify keys that are allowed to be replaced.*

```
    /**
     * Allowed config keys which can be inserted into email templates
     * eg use ##config.app.name## in the email template for automatic replacement.
     */
    'config_keys' => [
        'app.name',
        'app.url',
        'email-templates.customer-services'
```

### Implementing out of the box templates

[](#implementing-out-of-the-box-templates)

Emails may be sent directly, via a notification or an event listener.

The following email templates are included to get you started and show different methods of sending.

- **User Registered** - Welcome them to the platform
- **User Verify Email** - Check they are human
- **User Verified Email** - Yes they are
- **User Request Password Reset** - Let them change the password
- **User Password Reset Success** - Yay, you changed your password
- **User Locked Out** - Oops - What to do now?
- **User Login** - Success

Not all systems will require a login notification, but it's good practice for security so included here.

#### New User Registered Email

[](#new-user-registered-email)

A new **Registered** event is triggered when creating a new user.

We want to welcome new users with a friendly email so we've included a listener for the Illuminate\\Auth\\Events\\Registered Event which will send the email if enabled in the config:-

```
  'send_emails'             => [
        'new_user_registered'    => true,
        'verification'           => true,
        'user_verified'          => true,
        'login'                  => true,
        'password_reset_success' => true,
    ],
```

#### User Verify Email

[](#user-verify-email)

This notification is built in to Laravel so we have overidden the default toMail function to use our custom email template.

For reference this is done in the `EmailTemplatesAuthServiceProvider`.

> **Important** Register this provider so the override takes effect. Add `Visualbuilder\EmailTemplates\EmailTemplatesAuthServiceProvider::class`to the `providers` array in `config/app.php` (or within your own `AppServiceProvider`). Without this, Laravel will send its default verification email instead of your customised template.

This can be disabled in the config.

To Enable email verification ensure the User model implements the Laravel MustVerifyEmail contract:-

```
class User extends Authenticatable implements MustVerifyEmail
```

and include the **verified** middleware in your routes.

If you have a custom registration page and need to manually generate the verification URL, you can send the notification like this:

```
use Illuminate\Support\Facades\URL;

$notification = new \Filament\Notifications\Auth\VerifyEmail();
$notification->url = URL::temporarySignedRoute(
    'filament.actor.auth.email-verification.verify',
    now()->addMinutes(config('auth.verification.expire', 60)),
    [
        'id' => $user->getKey(),
        'hash' => sha1($user->getEmailForVerification()),
    ]
);

$user->notify($notification);

Auth::login($user);
```

> **Note** The `notify()` call must occur **before** logging in the user.

#### User Request Password Reset

[](#user-request-password-reset)

Replacing the Filament default email requires extending the Filament RequestPasswordReset class to override the default request method like this:-

```
namespace App\Filament\Resources\Auth;

use Visualbuilder\EmailTemplates\Notifications\UserResetPasswordRequestNotification;

class RequestPasswordReset extends \Filament\Pages\Auth\PasswordReset\RequestPasswordReset
{
    public function request(): void
    {
        try {
            $this->rateLimit(2);
        } catch (TooManyRequestsException $exception) {
            $this->getRateLimitedNotification($exception)?->send();
            return;
        }

        $data = $this->form->getState();

        $status = Password::broker(Filament::getAuthPasswordBroker())->sendResetLink(
            $data,
            function (CanResetPassword $user, string $token): void {
                if (! method_exists($user, 'notify')) {
                    $userClass = $user::class;
                    throw new Exception("Model [{$userClass}] does not have a [notify()] method.");
                }
                $tokenUrl = Filament::getResetPasswordUrl($token, $user);

                /**
                * Use our custom notification is the only difference.
                */
                $user->notify( new UserResetPasswordRequestNotification($tokenUrl));
            },
        );

        if ($status !== Password::RESET_LINK_SENT) {
            Notification::make()
                ->title(__($status))
                ->danger()
                ->send();

            return;
        }

        Notification::make()
            ->title(__($status))
            ->success()
            ->send();

        $this->form->fill();
    }
}
```

And then add this class into the admin panel provider:-

```
use App\Filament\Resources\Auth\RequestPasswordReset;

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path('admin')
            ->plugins([
                EmailTemplatesPlugin::make(),
            ])
            ->passwordReset(RequestPasswordReset::class)
```

### User Password Reset Success Notification

[](#user-password-reset-success-notification)

```
use Visualbuilder\EmailTemplates\Notifications\UserResetPasswordRequestNotification;

/**
     * @param $token
     *
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $url = \Illuminate\Support\Facades\URL::secure(route('password.reset', ['token' => $token, 'email' =>$this->email]));

        $this->notify(new UserResetPasswordRequestNotification($url));
    }
```

### Customising the email template

[](#customising-the-email-template)

Some theme colour options have been provided. Email templates will use the default theme unless you specify otherwise on the email template.

In the config file `config/filament-email-templates.php` logo, contacts, links and admin preferences can be set

```
    //Default Logo
    'logo'                    => 'media/email-templates/logo.png',

    //Logo size in pixels -> 200 pixels high is plenty big enough.
    'logo_width'              => '476',
    'logo_height'             => '117',

    //Content Width in Pixels
    'content_width'           => '600',

    //Contact details included in default email templates
    'customer-services'  => ['email' => 'support@yourcompany.com',
                             'phone' => '+441273 455702'],

    //Footer Links
    'links'                   => [
        ['name' => 'Website', 'url' => 'https://yourwebsite.com', 'title' => 'Goto website'],
        ['name' => 'Privacy Policy', 'url' => 'https://yourwebsite.com/privacy-policy', 'title' => 'View Privacy Policy'],
    ],
```

If you wish to directly edit the template blade files, see the primary template here:

- **Path**: `resources/views/vendor/vb-email-templates/email/default.php`

New templates in this directory will be automatically visible in the email template editor dropdown for selection.

#### Useful Tip

[](#useful-tip)

Not all email clients (e.g., Outlook) render CSS from a stylesheet effectively. To ensure maximum compatibility, it's best to **put styles inline**. For checking how your email looks across different clients, [Litmus Email Previews](https://www.litmus.com/landing-page/email-previews) is highly recommended.

### Translations

[](#translations)

Each email template is identified by a key and a language:

- **Key**: `user-password-reset`
- **Language**: `en_gb`

This allows the relevant template to be selected based on the users locale - You will need to save the users preferred language to implement this.

Please note laravel default locale is just "en" we prefer to separate British and American English so typically use en\_GB and en\_US instead but you can set this value as you wish.

Languages that should be shown on the language picker can be set in the config

```
    'default_locale'   => 'en_GB',

    //These will be included in the language picker when editing an email template
    'languages'        => [
        'en_GB' => ['display' => 'British', 'flag-icon' => 'gb'],
        'en_US' => ['display' => 'USA', 'flag-icon' => 'us'],
        'es'    => ['display' => 'Español', 'flag-icon' => 'es'],
        'fr'    => ['display' => 'Français', 'flag-icon' => 'fr'],
        'in'    => ['display' => 'Hindi', 'flag-icon' => 'in'],
        'pt'    => ['display' => 'Brasileiro', 'flag-icon' => 'br'],
    ]
```

[![Language Picker](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/Languages.png)](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/Languages.png)

Flag icons are loaded from CDN: see

### Creating new Mail Classes

[](#creating-new-mail-classes)

We've currently opted to keep using a separate Mailable Class for each email type. This means when you create a new template in the admin, it will require a new php Class. The package provides an action to build the class if the file does not exist in app\\Mail\\VisualBuilder\\EmailTemplates.

[![Build Class](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/BuildClass.png)](https://raw.githubusercontent.com/visualbuilder/email-templates/5.x/media/BuildClass.png)Currently generated Mailable Classes will use the BuildGenericEmail Trait

```
