RailsNotes UI Email Templates & Components
Getting Started
These docs walk you through adding, updating and using the ActionMailer component library in your Ruby on Rails apps.
Requirements
Each ActionMailer UI component is a ViewComponent under the hood. Make sure
you have the latest version of the ViewComponent gem installed by adding it to your Gemfile
—
# Should be included in :development, :test and :production groups
bundle add view_component
Download the components.zip
file (recommended)
Downloading the components.zip
file is the easiest way to get started with these ActionMailer components. Visit
the components page and click the [Download .zip]
button in the ActionMailer components section (make
sure
you're logged in).
components.zip
includes everything you need to get started -
components/
, which contains every ActionMailerEmail::
component.templates/
, which contains every ActionMailer template. These are great starting points for your mailer templates.
Copy the components/
folder from components.zip
into the app/components
directory of your Ruby on Rails app.
Note: If you're already using ViewComponents, you already have an
app/components/
folder. In that case, just copy thecomponents/email
folder.
This will make the components available in ALL your views (not just your mailer templates). You probably won't
use these outside of your emails though, hence the Email::
namespace.
Copy individual components (alternative)
If you don't want to download the components.zip
, or if you want to grab the "stock" version of a component (perhaps
you
went a bit too gung-ho with modifying one 😅), you can do that too.
The source code for each component can be copied directly. Visit the components page and navigate to the components you want to download.
The following components are REQUIRED —
base.rb
— all other components inherit from::Base
colors.rb
— most components use colors from this modulereset_stylesheet.rb
— each::Container
will try to render this module- One of the
::Container
components (full
,plain
orbg
) — all other email components must be rendered into a::Container
component.
Copy each of these components into a correctly named file inside the app/components/email
folder of your Ruby on Rails
app.
Note: I highly recommend you just grab the
components.zip
file and copy the components across that way. It's much easier and will save you some headaches.
Build your mailers
Once you've installed the components correctly, they'll be available in your mailer views under the Email::
namespace. For example, the button component is available as Email::Button
.
For more information, I recommend you look at the included mailer templates, which will show you how to correctly use these components (plus some more advanced techniques).
Updating components
Occasionally, the RailsNotes UI ActionMailer component library will be updated. New templates may also be released.
Some updates may include breaking changes to components. I do my best to minimize these (I know that patching components can be a pain!), but they might be needed for bug fixes or major new features. See the changelog for info on the latest updates.
You're never forced to update your components. The code is already in your app! However, keeping your components up to date will give you bug fixes and improvements. New templates will also use the latest versions of components.
If you follow the recommended way to update — overwriting all your components with their new versions — updating should be painless. I strongly recommend you follow the recommended update method.
Overwriting old components (recommended)
The recommended way to update your components, as with installing them, is to download the
new components.zip
file and replace the entire app/components/email
folder in your app. Just copy
the components/email
folder from the new components.zip
file into the app/components
folder of your Ruby on
Rails app, and overwrite the old app/components/email
folder.
Replacing the app/components/email
folder in your Ruby on Rails app will overwrite any edits you made to your
component files! (but not your mailer templates).
Updating individual files (alternative)
You can update components manually by copying across their new versions. You might do this if you've directly
edited some components (ie: edited text.rb
), and want to selectively update your components to preserve their edits.
You can do this by visiting each component's page, and copying across the updated code.
In general, I recommend you avoid updating your components manually (file-by-file). It's error-prone and may lead to bugs. It's a lot easier to copy across the new files from
components.zip
, and will help preserve your sanity.
Any new templates will also be included in a new version of the components.zip
file. As usual, you can also grab them
from the RailsNotes UI website.
Why make updating so convoluted? Why no gem?
I want to give you full control over your email components. Emails are a core part of your app! I want to give you the power to dive deep into your components and see what's going on — packaging them as a gem would hinder this. It would also force you to rely on me to keep the gem server live.
By giving you the code directly, you get full control over your components and aren't forced to rely on me. The tradeoff is that updates become a bit more involved.
In general, I plan to avoid updating old components where I can help it! I get that it's cumbersome to update your components (despite my best efforts to streamline things). For bug fixes though, there's not really another option.
As long as you don't edit component files directly, updating should be pretty painless. Just copy across the new components, overwrite the old ones, and (possibly) tweak your custom mailer templates.
Building your mailer templates
This section is a mini-teardown of a typical email template built with the ActionMailer component library. This will help you build your own mailer templates.
A typical mailer template
Here's a basic mailer template, built with RailsNotes UI components —
<%= render Email::Bg::Container.new do |email| %>
<% email.with_masthead_text(text: "RailsNotes UI", href: "https://railsnotesui.xyz") %>
<%# Headings and some body text %>
<%= render Email::Heading.new(text: "Thanks for joining!", align: "center") %>
<%= render Email::Text.new(text: "Thank you for joining my mailing list!") %>
<%# Button inside a gray container %>
<%= render Email::Colorblock.new(color: Email::Colors::GRAY_50) do %>
<%= render Email::Button.new(
text: "Buy something now →",
href: "https://example.com/checkout"
) %>
<% end %>
<%# Text with a link. We pass a block to Email::Text, so we can use the link_to helper %>
<%= render Email::Text.new do %>
If you have any questions, send an email
to <%= link_to "[email protected]", "[email protected]" %>.
<% end %>
<%# The ::Footer is optional, and must be the last component inside the container. %>
<%= render Email::Footer.new do %>
© RailsNotes 2023
<% end %>
<% end %>
What's going on here?
- All mailer templates start with a
::Container
. The container wraps all the other parts of our email. In this case, we use theEmail::Bg::Container
component. - We then render an
Email::MastheadText
component by callingemail.with_masthead_text
. This is ViewComponent Slot Syntax. A masthead is optional and sits above the main email container. - We then render some text components by passing a
text:
string to them directly. - Next, we render an
Email::Colorblock
. The::Coloblock
component wraps other email components and renders them inside a colored container. We override the default color withEmail::Colors::GRAY_50
, and pass in a block containing a button. - Next, we render some more text, but this time we pass
::Text
a block. This lets us use thelink_to
helper to generate a link. - Finally, we render a
::Footer
at the end of our mailer template. Rendering a footer is optional, but it must go at the end of your mailer template (if you use it).
That's it!
Override default styles in base.rb
The Email::Base
component, located in base.rb
, is unique and worth looking at.
This component is where we define default styles for most other components.
For example, we define default styles for the Email::Text
component inside the TextStyles
class -
class Email::Base < ViewComponent::Base
# ...
class TextStyles
SIZE = "16px"
COLOR = Email::Colors::GRAY_900 # [dark variant available]
end
end
Inside the Email::Text
component (see text.rb
), we inherit the default styles -
class Email::Text < Email::Base
def initialize(
# ...
size: TextStyles::SIZE,
color: TextStyles::COLOR,
)
@size = size
@color = color
end
end
This gives us a way to override the global styles of individual components. If you want to make your body
text a bit larger, rather than passing size: "18px"
each time you create an Email::Text
component, you can adjust it
directly in base.rb
. You would just set TextStyles.SIZE = '18px'
.
I go over this a bit more in the next section, so if you're feeling a bit lost or confused, read on. You can also email me directly at [email protected] if you're having trouble.
Default Values, Colors::
and Dark Mode.
Component Defaults
Most components only have 1-2 required parameters. The other parameters are set by default but can be overridden if you want to customize your components more thoroughly.
For example, look at the Email::Button
component —
# app/components/email/button.rb
# #
class Email::Button < Email::Base
def initialize(
text:,
href:,
color: ButtonStyles::BACKGROUND_COLOR, # inherited from Email::Base
text_color: ButtonStyles::TEXT_COLOR, # inherited from Email::Base
margin: "30px 0 30px 0",
border_radius: "8px"
)
...
end
...
end
The Email::Button
component only requires the text:
and href:
attributes. Everything else has a default value,
but you can override them!
Overriding default values
Here are some examples of rendering components and overriding their default values (using the Email::Button
component).
In the first example, we don't pass in any parameters, which raises an ArgumentError
. In the second example, we
only pass the required parameters, and in the third example, we override a default value.
# no params, raises ArgumentError, "missing keyword"
#
Email::Button.new
# required params only
#
Email::Button.new(
text: "Click me",
href: "https://example.com"
)
# overriding an optional param
#
Email::Button.new(
text: "Click me",
href: "https://example.com",
color: Email::Colors::GREEN_500 # using the included Colors:: module
)
For more examples check out the example templates, or the file-level comments for each component (which include some usage examples).
Default Values
You can edit the Email::Base
class to customize the default styles of your components. For example, you could change
all your buttons to be green.
The Email::Base
class (inside components/email/base.rb
) holds default values for all other components. These
values then get inherited by each component.
For example, this is how the default background color of a button is set to Email::Colors::RED_500
—
# Email::Base contains the default styles for most components
#
class Email::Base < ViewComponent::Base
...
class ButtonStyles
BACKGROUND_COLOR = Email::Colors::RED_500 # [dark variant available]
...
end
...
end
If you wanted to change the default button color to green, you could do BACKGROUND_COLOR = Email::Colors::GREEN_500
.
Dark Mode
Some components support dark variants, however, they can only be customized globally inside base.rb
. This is
because
we insert dark variants as @media
queries into the Email::Container
components.
Inside base.rb
, the styling for dark variants lives inside the DarkStyles
class —
# Styles for dark mode rendering
# NOTE: These styles can _only_ be configured here (due to how we handle media queries).
# You can't adjust the dark mode display for individual elements.
class DarkStyles
# whether dark mode media queries are included in your emails
# set this false to disable dark mode
# useful if your mailer templates use dark colors even in "light" mode
ENABLED = true
# Email::Container
CONTAINER_COLOR = Email::Colors::GRAY_800
CONTAINER_SURROUNDS_COLOR = Email::Colors::GRAY_900
CONTAINER_TEXT_COLOR = Email::Colors::GRAY_100
# Email::Button
BUTTON_COLOR = Email::Colors::GRAY_100
BUTTON_TEXT_COLOR = Email::Colors::GRAY_900
# Email::Colorblock
COLORBLOCK_COLOR = Email::Colors::GRAY_900
end
If you want to change how your email looks in dark mode, you need to edit these values.
You can also completely disable dark mode by setting ENABLED=false
. This will remove all the @media
queries
associated with dark mode from the ::Container
components. This can be useful if you're building a template that's
dark by default, and dont want anything to change on a dark color scheme.
Colors:: module
I've added a Colors::
module into the project, to save you looking up #hex
codes all day.
This module is based on the TailwindCSS color palette and uses naming
like Email::Colors::WHITE
and Email::Colors::RED_500
to correspond to hex strings.
Using this module is optional! But I find it very handy — It's a great way to quickly tweak the colors in your email, and gives you a good starting point.
If you prefer to use #hex
codes though, you can! Anywhere you see a Colors::
value, you can replace it with a
plain #hex
string. If you peek inside colors.rb
, you'll see that this module just maps names
like Email::Colors::RED_500
to their matching hex strings.
Top-level documentation
Most components have documentation and examples as file-level comments. The example code should be copy-pastable straight into your ERB templates.
Here's the comment at the top of the Email::Button
component -
# Email::Button component [supports dark variant]
# REQUIRED: text:, href:
# NOTES:
# - href: needs to be an absolute url (good="https://example.com", bad="example.com")
#
#
# Example 1 (simple):
# <%= render Email::Button.new(
# text: "Simple Button",
# href: "https://example.com",
# ) %>
#
# Example 2 (override styles):
# <%= render Email::Button.new(
# text: "Advanced button → ",
# href: "https://example.com",
# color: Email::Colors::ORANGE_500,
# text_color: Email::Colors::ORANGE_50,
# text_size: "20px",
# width: "48px",
# height: "16px",
# border_radius: "60px",
# margin: "60px auto 0 auto"
# ) %>