Search by

ghadeer / lara-mail-preview

GhadeerHamed

Secure, configurable browser previews for Laravel mailables.

Package info

github.com/GhadeerHamed/lara-mail-preview

pkg:composer/ghadeer/lara-mail-preview

Statistics

Installs: 17

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v2.0.4 2026-08-31 10:13 UTC

This package is auto-updated.

Last update: 2026-08-31 10:14:19 UTC


README

Preview Laravel mailables in the browser without delivering email. Lara Mail Preview is intended for local development and protected development environments.

Requirements

Package version Laravel PHP
2.x 12.x, 13.x 8.2–8.5 (Laravel 13 requires 8.3+)

Install

composer require ghadeer/lara-mail-preview
php artisan vendor:publish --tag=mail-preview-config

The preview route is enabled only in the local environment by default. Visit /mail-preview after installing. To use it on a shared development system, add authentication or your own authorization middleware first:

'environments' => ['local', 'development'],
'middleware' => ['web', 'auth', 'can:preview-mail'],

Never add a public route or enable this package in production. Rendering a mailable may execute application code, query data, or use external services.

Configuration

config/mail-preview.php controls the route, enabled environments, middleware, discovery locations and model selects. Mailables in app/Mail are discovered when they are Composer-autoloadable and extend Illuminate\Mail\Mailable.

For mailables outside that location, or to make the allow-list completely explicit, configure them directly:

'mailables' => [
    App\Mail\WelcomeMail::class,
    App\Mail\Billing\InvoiceMail::class => ['label' => 'Invoice'],
],

'paths' => [
    app_path('Mail'),
    'Domain\\Billing\\Mail' => app_path('../src/Billing/Mail'),
],

Only discovered or configured mailables can be previewed. Requests cannot instantiate arbitrary class names.

Complex mailables

The UI safely supports strings, integers, floats, booleans, JSON arrays, Eloquent models and backed/pure enums. Complex objects, union types and intersection types need a trusted preview scenario:

'mailables' => [
    App\Mail\ReceiptMail::class => [
        'label' => 'Latest receipt',
        'scenario' => function (): App\Mail\ReceiptMail {
            return new App\Mail\ReceiptMail(
                App\Models\Receipt::query()->latest()->firstOrFail(),
            );
        },
    ],
],

The scenario is application code and is trusted by definition. Prefer an invokable class or static method if your application caches configuration.

Model inputs

Model choices are capped at 100 by default and use the first available label column (name, title, description, author, email, last_name, first_name, then the model key). Scope sensitive data with a configured query callable, or compose a human-friendly label with a callback:

'models' => [
    'queries' => [
        App\Models\Customer::class => fn (App\Models\Customer $customer) => $customer->newQuery()->where('account_id', auth()->user()->account_id),
    ],
    'label_callbacks' => [
        App\Models\Customer::class => fn (App\Models\Customer $customer) => "{$customer->first_name} {$customer->last_name} <{$customer->email}>",
    ],
],

Use authorization middleware as the primary access control; the query callback further limits which model records can be chosen.

Preview output

The page shows HTML, plain text and envelope metadata (subject, addresses and tags). HTML is placed in a sandboxed iframe so email styles and scripts cannot affect the preview application. This is a rendering isolation boundary, not a reason to expose untrusted emails publicly.

Development

composer update
composer test

The GitHub Actions matrix runs Laravel 12 on PHP 8.2 and Laravel 13 on PHP 8.3–8.5.

Releasing

This repository uses Git tags as the package version source. Do not add a Composer version field for Packagist/VCS releases. After merging a release, create and push a semantic tag, for example v2.0.0.