Weave Code
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

Emogrifier Laravel Package

pelago/emogrifier

Emogrifier converts CSS from blocks and stylesheets into inline style attributes in HTML. Ideal for HTML email rendering in clients with poor CSS support (e.g., Outlook, Gmail), ensuring consistent styling across email and mobile readers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require pelago/emogrifier
    

    Add to composer.json under require if preferred.

  2. Basic Inlining: For HTML emails or mobile-optimized content, inline CSS directly:

    use Pelago\Emogrifier\CssInliner;
    
    $html = '<html><body><p class="email-text">Hello</p></body></html>';
    $css = '.email-text { color: red; }';
    
    $inlinedHtml = CssInliner::fromHtml($html)->inlineCss($css)->render();
    

    Output: <p style="color: red;">Hello</p> (with proper HTML structure preserved).

  3. No External CSS? Skip the $css parameter to parse <style> blocks in the HTML:

    $inlinedHtml = CssInliner::fromHtml($html)->inlineCss()->render();
    
  4. First Use Case:

    • Email Templates: Convert styled HTML emails to inline CSS for compatibility with Outlook/Gmail.
    • Legacy Systems: Ensure CSS works on devices/browsers with limited support (e.g., older mobile browsers).

Implementation Patterns

Core Workflow

  1. Preprocessing: Normalize HTML structure (add missing <head>, <body>, fix nesting):

    use Pelago\Emogrifier\HtmlProcessor\HtmlNormalizer;
    $normalizedHtml = HtmlNormalizer::fromHtml($rawHtml)->render();
    
  2. Inlining CSS:

    • External CSS: Pass as a string to inlineCss($css).
    • Embedded CSS: Omit the argument to parse <style> blocks.
    • Options: Chain modifiers like disableStyleBlocksParsing() or addExcludedSelector().
  3. Post-Processing:

    • Cleanup: Remove display: none elements or redundant classes:
      $prunedHtml = HtmlPruner::fromDomDocument($dom)
        ->removeElementsWithDisplayNone()
        ->removeRedundantClassesAfterCssInlined($cssInliner)
        ->render();
      
    • Visual Attributes: Convert CSS to HTML attributes (e.g., width: 100pxwidth="100"):
      $visualHtml = CssToAttributeConverter::fromDomDocument($dom)
        ->convertCssToVisualAttributes()
        ->render();
      
  4. CSS Variables: Resolve var(--variable) in inline styles:

    $evaluatedHtml = CssVariableEvaluator::fromDomDocument($dom)
      ->evaluateVariables()
      ->render();
    

Integration Tips

  • Laravel Blade: Create a helper function in app/Helpers/EmailHelper.php:

    use Pelago\Emogrifier\CssInliner;
    
    if (!function_exists('emogrify')) {
        function emogrify($html, $css = null) {
            return CssInliner::fromHtml($html)->inlineCss($css)->render();
        }
    }
    

    Use in Blade:

    {!! emogrify($emailTemplate, $styles) !!}
    
  • Service Provider: Register a service to handle email templating:

    // app/Providers/EmailServiceProvider.php
    public function register() {
        $this->app->singleton('emogrifier', function () {
            return new CssInliner();
        });
    }
    
  • Queue Jobs: For async processing (e.g., sending emails):

    // app/Jobs/ProcessEmail.php
    public function handle() {
        $html = $this->emogrifier->fromHtml($this->template)->inlineCss($this->styles)->render();
        Mail::send([...], $html);
    }
    
  • Testing: Use renderBodyContent() to isolate body content for unit tests:

    $body = CssInliner::fromHtml($html)->inlineCss()->renderBodyContent();
    $this->assertStringContainsString('style="color: red;"', $body);
    

Gotchas and Tips

Pitfalls

  1. Selector Specificity:

    • Partial Matches: addExcludedCssSelector('.example') won’t exclude p .example. Use universal selectors (e.g., .example, * .example) for broader exclusion.
    • Pseudo-Classes: :hover, :active, etc., are not supported. Avoid dynamic styles.
  2. Media Queries:

    • Default allowed media types: all, screen, print. Add others with addAllowedMediaType().
    • Outlook Quirk: Outlook ignores @media entirely. Exclude critical styles with addExcludedCssSelector().
  3. HTML Structure:

    • Missing Tags: HtmlNormalizer adds <head>/<body> if absent, but may alter structure. Test edge cases (e.g., <div><p></div>).
    • Script/Style Blocks: Inlined <style> blocks are removed by default. Use disableStyleBlocksParsing() to preserve them.
  4. CSS Variables:

    • Scope: Variables must be defined in :root or an ancestor element to be resolved.
    • Fallbacks: Unresolved variables (e.g., var(--undefined)) remain as-is.
  5. Performance:

    • Large HTML: Complex selectors (e.g., div.container > ul li:nth-child(2)) slow processing. Optimize selectors or use addExcludedSelector() for non-critical elements.
    • Memory: DOM manipulation can be heavy. For batch processing, reuse DOMDocument instances:
      $dom = new DOMDocument();
      $dom->loadHTML($html);
      $cssInliner = CssInliner::fromDomDocument($dom)->inlineCss($css);
      

Debugging Tips

  1. Inspect Output: Use render() to see intermediate HTML. For debugging, log the DOM:

    $dom = $cssInliner->getDomDocument();
    file_put_contents('debug.html', $dom->saveHTML());
    
  2. Selector Testing: Test selectors in isolation:

    $html = '<div class="test">Hello</div>';
    $css = '.test { color: red; }';
    $result = CssInliner::fromHtml($html)->inlineCss($css)->render();
    
  3. Common Issues:

    • Styles Not Applied: Check for typos in selectors or excluded selectors.
    • Broken Layout: Use HtmlNormalizer to fix malformed HTML.
    • Outlook-Specific Bugs: Test with Litmus or Email on Acid.

Extension Points

  1. Custom Processors: Extend AbstractHtmlProcessor to add steps (e.g., sanitization):

    class CustomProcessor extends AbstractHtmlProcessor {
        public function addCustomAttribute() {
            $this->dom->documentElement->setAttribute('data-processed', 'true');
            return $this;
        }
    }
    
  2. Selector Extensions: Override getSupportedSelectors() in a custom CssInliner class to support unsupported selectors (e.g., :nth-child).

  3. Post-Inlining Hooks: Use getDomDocument() to manipulate the DOM after inlining:

    $dom = $cssInliner->getDomDocument();
    $xpath = new DOMXPath($dom);
    foreach ($xpath->query('//img') as $img) {
        $img->setAttribute('alt', 'Fallback');
    }
    
  4. Configuration: Centralize options in a config file (e.g., config/emogrifier.php):

    return [
        'allowed_media' => ['all', 'screen', 'print', 'handheld'],
        'excluded_selectors' => ['.ad-banner', '.ad-banner *'],
    ];
    

    Load in a service provider:

    $cssInliner->addAllowedMediaType(config('emogrifier.allowed_media'));
    

Pro Tips

  1. Email-Specific Optimizations:

    • Tables: Use <table> for layout (better email client support). Inline border-collapse and width attributes.
    • Fallbacks: Add !important to critical styles (e.g., color: red !important;).
  2. Caching: Cache inlined HTML for static emails:

    $cacheKey = md5($html . $css);
    $inlinedHtml = Cache::remember($cacheKey, now()->addHours(1), function() use ($html, $css) {
        return CssInliner::fromHtml($html)->inlineCss($css)->render();
    });
    
  3. Testing Framework: Create a trait for Laravel tests:

    trait EmogrifierTest {
        protected function assertInlinedStyles($expectedStyles, $html) {
            $inlined = CssInliner::fromHtml($html
    
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.
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon
itsemon245/lamet
baks-dev/dashboard
amoifr/pickle-panther-bundle
make-dev/orca
dmstr/symfony-system-resources-bundle
dmstr/symfony-job-queue-bundle
dmstr/openapi-json-schema-bundle
dmstr/keycloak-security-bundle
dmstr/doctrine-audit-log-bundle