premailer-rails

repository·main·Indexed 23 days ago

https://github.com/fphilipe/premailer-rails

An adapter for the premailer gem that provides automatic CSS inlining for ActionMailer emails. It converts standard HTML and external CSS files into inline style attributes to ensure email client compatibility. The gem supports multiple CSS retrieval strategies, including filesystem, asset pipeline, and network requests, and integrates with ActionMailer via a Railtie for automatic processing.

Tokens
1.7K
Snippets
9
Records
12
Agent score
74%

What's inside premailer-rails

  1. How premailer-rails inlines CSS

    main

    The gem works by registering a delivery hook with actionmailer. When an email is delivered, premailer-rails collects URLs from <link rel="stylesheet" href="..."> tags and attempts to retrieve their content using the following strategies in order:

    1. :filesystem: Looks for a file in the public/ directory matching the URL path.
    2. :asset_pipeline: If Rails is available and the asset pipeline is enabled, it strips the fingerprint and prefix from the URL to request the asset via the pipeline.
    3. :network: Performs a standard HTTP request to the URL (useful for assets hosted on a CDN).

    In Rails production environments, retrieved CSS is cached. To prevent specific tags (like Google Fonts) from being inlined, add the data-premailer="ignore" attribute to the tag.

  2. Setup premailer-rails in non-Rails environments

    main

    If you are using actionmailer outside of a Rails environment (e.g., in Sinatra), you must manually register the interceptors for the gem to function. This should be done in your application's initialization phase.

    Premailer::Rails.register_interceptors
  3. Install premailer-rails

    main

    Add premailer-rails to your Gemfile.

    Important: premailer-rails and premailer do not have a hard dependency on an HTML parser. You must manually add an HTML parsing gem to your project. This gem is primarily tested with nokogiri.

    gem 'premailer-rails'
  4. Configure premailer-rails options

    main

    You can pass configuration options to the underlying premailer instance by using Premailer::Rails.config.merge!. In a Rails application, this is typically done in an initializer (e.g., config/initializers/premailer_rails.rb).

    Default Configuration:

    {
      input_encoding: 'UTF-8',
      generate_text_part: true,
      strategies: [:filesystem, :asset_pipeline, :network]
    }

    Common Tasks:

    • Disable automatic text part generation: Set :generate_text_part to false.
    • Customizing strategies: Pass a custom array to :strategies to change the order or selection of CSS retrieval methods.
    Premailer::Rails.config.merge!(preserve_styles: true, remove_ids: true)
  5. Automatic integration with Action Mailer via Railtie

    main
    When included in a Rails application, premailer-rails automatically integrates with ActionMailer. It uses a Railtie to register interceptors during the action_mailer load cycle. This ensures that email content is automatically processed (premailed) whenever an email is sent through Action Mailer, without requiring manual interceptor registration in your application code.
  6. Integrate premailer-rails with ActionMailer

    main
    To automatically inline CSS in your emails using premailer-rails, call Premailer::Rails.register_interceptors. This method registers the Premailer::Rails::Hook as an interceptor for ActionMailer::Base. If your version of ActionMailer supports it, it will also register the hook for email previews.
  7. Skip premailer for specific emails

    main

    To prevent premailer-rails from processing a specific email, set the :skip_premailer header to true in the mail method call.

    Warning: The mere presence of the skip_premailer header causes the gem to skip processing. Because headers are transformed into strings, setting skip_premailer: false will still result in the email being skipped because the string 'false' is truthy.

    class UserMailer < ActionMailer::Base
      def welcome_email(user)
        mail to: user.email,
             subject: 'Welcome to My Awesome Site',
             skip_premailer: true
      end
    end
  8. Manually trigger CSS inlining

    main

    Emails are processed automatically upon delivery or when previewing in Rails. If you need to trigger the inlining manually (for example, during testing), you can call Premailer::Rails::Hook.perform(mail). This modifies the email object in place.

    mail = SomeMailer.some_message(args)
    Premailer::Rails::Hook.perform(mail)
  9. Configure premailer-rails settings

    main

    You can access and modify the global configuration via Premailer::Rails.config. The default configuration includes the following keys:

    • input_encoding: The encoding of the input HTML (default: 'UTF-8').
    • generate_text_part: Whether to generate a plain text part for the email (default: true).
    • strategies: An array of CSS loading strategies to use (default: [:filesystem, :asset_pipeline, :propshaft, :network]).
    Premailer::Rails.config = {
      input_encoding: 'UTF-8',
      generate_text_part: true,
      strategies: [:filesystem, :asset_pipeline, :propshaft, :network]
    }
  10. Extend Premailer behavior with CustomizedPremailer

    main

    If you need to override the default behavior of Premailer (for example, to inject custom CSS logic or modify how HTML is parsed before the premailing process begins), you can subclass Premailer::Rails::CustomizedPremailer.

    This class is designed to facilitate passing a CSS string to the parent Premailer class by first initializing an adapter and parsing the HTML. It automatically merges Rails.config with the option with_html_string: true and ensures a suitable adapter is included to allow load_html to function correctly.

    class Premailer
      module Rails
        class CustomizedPremailer < ::Premailer
          def initialize(html)
            @options = Rails.config.merge(with_html_string: true)
            Premailer.send(:include, Adapter.find(Adapter.use))
            doc = load_html(html)
            options = @options.merge(css_string: CSSHelper.css_for_doc(doc))
    
            super(doc.to_s, options)
          end
        end
      end
    end