Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

To Inline Style Email Bundle Laravel Package

gushphp/to-inline-style-email-bundle

A Symfony bundle that inlines CSS in HTML email templates for better rendering across email clients. It wraps the to-inline-style-email library and adds Symfony service and configuration integration, helping prevent stripped or ignored styles in Gmail, Outlook, and other clients.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the honest framing: this is a Symfony 2 bundle, not a Laravel package. It exists to (a) register TijsVerkoyen\CssToInlineStyles\CssToInlineStyles in the Symfony DI container and (b) hook it into the mailer so outgoing messages get their <style> blocks inlined before send. In a Laravel app you get the same translator for free — tijsverkoyen/css-to-inline-styles is a direct dependency of illuminate/mail, and Laravel's Markdown mailables already run it over the rendered HTML.

So the "getting started" path for a Laravel dev is:

# Do NOT require the bundle. Confirm the translator is already present:
composer show tijsverkoyen/css-to-inline-styles

If it isn't (very old Laravel, or you've pruned it):

composer require tijsverkoyen/css-to-inline-styles:^2.2

Where to look first: vendor/tijsverkoyen/css-to-inline-styles/src/CssToInlineStyles.php. The whole API is essentially:

$inliner = new \TijsVerkoyen\CssToInlineStyles\CssToInlineStyles();
$html = $inliner->convert($htmlString, $cssString); // $css optional

First use case: a Blade mailable whose markup references a stylesheet you'd rather not hand-inline. Render the view, feed it through convert(), and set the result as the body:

class InvoiceShipped extends Mailable
{
    use Queueable, SerializesModels;

    public function build()
    {
        $html = view('emails.invoice-shipped', ['order' => $this->order])->render();
        $css  = file_get_contents(resource_path('css/email.css'));

        return $this->subject('Your order shipped')
            ->html((new CssToInlineStyles())->convert($html, $css));
    }
}

If you're actually maintaining a Symfony 2/3 app that depends on this bundle, the install path is the classic one: composer require gushphp/to-inline-style-email-bundle, register it in AppKernel::registerBundles(), then check DependencyInjection/ in the bundle source for the service ID and any strip_original_styles / cleanup style config keys before wiring it into your mailer.

Implementation Patterns

1. Lean on Markdown mailables instead. For any new email work, php artisan make:mail Foo --markdown=emails.foo gives you CSS inlining, a responsive table layout, and theming for free. Publish and edit the theme rather than writing an inliner call:

php artisan vendor:publish --tag=laravel-mail
# resources/views/vendor/mail/html/themes/default.css

This is the pattern that makes the bundle redundant for 80% of cases.

2. Trait for classic Blade mailables. If you have an existing fleet of non-Markdown mailables, put the inlining in one place:

trait InlinesStyles
{
    protected function inlineHtml(string $view, array $data = [], ?string $css = null): string
    {
        $html = view($view, $data)->render();
        $css ??= file_get_contents(resource_path('css/email.css'));

        return (new CssToInlineStyles())->convert($html, $css, 'UTF-8');
    }
}

Then ->html($this->inlineHtml('emails.receipt', compact('order'))) in build().

3. Event-listener approach (closest analogue to the bundle's mailer hook). If you want inlining applied globally without touching every Mailable — which is what the Symfony bundle does via mailer events — listen for MessageSending:

// app/Listeners/InlineEmailStyles.php
public function handle(MessageSending $event): void
{
    $message = $event->message;
    if (! $message->getBody()->hasHtmlBody()) {
        return;
    }
    // Extract <style> blocks, run convert(), replace the HTML part.
}

Register it in EventServiceProvider. The caveat is real: parsing and rewriting the HTML part of a Symfony\Component\Mime\Email is fiddlier than doing it at render time, and you must not clobber the text part.

4. Inline before queueing. Do the conversion inside build() rather than in a sending callback. Queued mailables serialize the built message; if you inline late, a worker re-runs the CSS read (and any file_get_contents failure surfaces in the queue instead of at dispatch). Build once, freeze the HTML.

5. Reuse for previews. The same convert() call powers a dev-only preview route — useful for eyeballing what Gmail will actually receive:

Route::get('/dev/mail-preview/{mailable}', fn ($mailable) => ...);

Gate it behind App::environment('local').

6. Symfony bundle usage (if you're genuinely on Symfony). Inject the inliner service into your mailer/controller and call convert() on the rendered Twig output; the bundle's value-add is the container wiring, not new behavior. Check the bundle's Resources/config/services.* for the exact ID and whether it also registers a Swift Mailer plugin.

7. Keep the media queries. Don't inline everything. The standard pattern is: inline the base styles, leave @media blocks and :hover/pseudo-selectors in a <style> tag in <head>, since those cannot be expressed as inline attributes.

Gotchas and Tips

Abandoned since 2017. Last release was 2017-06-17, targeting PHP 5.x / Symfony 2.x. On a modern Laravel it will fail dependency resolution (composer why-not gushphp/to-inline-style-email-bundle will show the Symfony constraint conflict). Zero dependents on Packagist is a strong signal nobody maintains the integration.

Don't double-inline. Laravel's Markdown pipeline already inlines. Running convert() over Markdown-rendered output is wasted CPU and can mangle the carefully-tuned theme output. Detect and skip: if you're using --markdown, don't call the trait.

Version pinning is the trap. Laravel pins a compatible tijsverkoyen/css-to-inline-styles range for illuminate/mail. Don't composer require an arbitrary newer major "just because" — you'll break Markdown mailables. Use composer why tijsverkoyen/css-to-inline-styles to see the constraint and stay inside it.

@media and pseudo-classes are silently lost. They can't be inlined, so if your CSS is only in a <style> block, the inliner drops them. Ship both: inline base rules plus a <style> head block for the responsive overrides.

Specificity inversion. Inlining promotes every rule to element-level, which flips the cascade: a rule you expected a <style> block to override may now lose. Write email CSS defensively — flat selectors, minimal !important.

Encoding. Pass 'UTF-8' explicitly as the third argument. Without it, non-ASCII content (accents, emoji, currency) can come back mojibake'd.

Malformed CSS throws. convert() can raise on bad CSS. It runs at render/send time, so an unrelated typo in a shared stylesheet takes down every email. Wrap the call, log, and fall back to the un-inlined HTML rather than 500-ing a queued job.

Cleanup option. The library's setCleanup() / setStripOriginalStyleTags() options let you remove original <style> tags post-conversion. Don't strip them if you kept media queries — you'll delete your responsive rules.

Extension points. Bind your own configured instance in a service provider so options live in one place:

$this->app->singleton(CssToInlineStyles::class, function () {
    return (new CssToInlineStyles())->setCleanup(false);
});

Testing. Assert on the outgoing message, not the view: Mail::fake() + Mail::assertSent(InvoiceShipped::class, fn ($m) => str_contains($m->render(), 'style="')). Cheaper smoke test for "did inlining survive a refactor."

Alternative libraries if you hit limits: pelago/emogrifier (more aggressive, handles more CSS), or the managed route (Premailer/Postmark's inlining API) if you'd rather not own it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin