Salut Courrier! 👋 📮
Courrier is a new Ruby gem to send emails in your apps. You write a class containing the subject, HTML, and/or plain text content. It then uses the API from your choice of transactional email provider like Mailgun, Postmark, and Resend.
⭐ Go straight to GitHub and give it that star! ⭐
courrier /ku.ʁje/ noun, masculine
- mail, post (postal items) ▪ le courrier du matin - morning mail ▪ relever le courrier - to collect the mail
- letter, correspondence ▪ répondre à un courrier - to reply to a letter ▪ courrier électronique - email
- messenger, courier (person) ▪ courrier diplomatique - diplomatic courier ▪ courrier à vélo - bike courier
Courrier is a gem extracted from my own (SaaS) apps. For years it lived as just one (😬) file in the app’s lib folder. From my Rails UI consultancy work and helping people start a SaaS with Rails, it became clear the concept of Courrier was hitting a nerve. 💡
So how does it work? Create a class that inherits from Courrier::Email
:
class OrderEmail < Courrier::Email
def subject = "Here is your order!"
def text
<<~TEXT
text body here
TEXT
end
def html
<<~HTML
html body here
HTML
end
end
You then send the email with:
OrderEmail.deliver to: "recipient@railsdesigner.com"
Isn’t that beautiful? ❤️
Need to pass on some contextual attributes? Use them in in your text or HTML, like so:
def text
<<~TEXT
#{download_url}
TEXT
end
And set them when delivering the email:
OrderEmail.deliver to: "recipient@railsdesigner.com",\
download_url: download_path(token: "token")
How to Configure Courrier
Courrier uses a configuration system with three levels (from lowest to highest priority).
Global Configuration
This is, typically in a Rails app, in config/initializer/courrier.rb
Courrier.configure do |config|
config.provider = "postmark"
config.api_key = "xyz"
config.from = "devs@railsdesigner.com"
config.default_url_options = { host: "railsdesigner.com" }
end
You can then override these settings per class:
class OrderEmail < Courrier::Email
configure from: "orders@railsdesigner.com",
cc: "records@railsdesigner.com",
provider: "mailgun",
end
Or even on an instance of the class (although you typically would only set the to
-attribute and some contextual values):
OrderEmail.deliver to: "recipient@railsdesigner.com",\
from: "shop@railsdesigner.com",\
provider: "sendgrid",\
api_key: "sk_a1b1c3"
If needed, you can quickly switch providers by setting the COURRIER_PROVIDER
and COURRIER_API_KEY
. You know, in case of emergencies. 🆘 😅
Consistent Results
Courrier returns a Result
object that is consistent for each provider.
delivery = OrderEmail.deliver to: "recipient@example.com"
if delivery.success?
puts "Email sent successfully!"
puts "Provider response: #{delivery.data}"
else
puts "Failed to send email: #{delivery.error}"
end
The available methods on Result
are:
success?
— returnstrue
if the API request was successful;response
— the raw HTTP response from the email provider;data
— parsed JSON response body from the provider;error
— contains any error that occurred during delivery.
But Courrier has quite a few more features to help you with development and testing. Let’s go over them. 👇
Delivery to STDOUT
By default Courrier outputs all sent emails to STDOUT
in development. It looks something like this:
--------------------------------------------------------------------------------
Timestamp: 2025-05-14 07:30:00 +0000
From: Support Team <devs@railsdesigner.com>
To: recipient@railsdesigner.com
Subject: Here is your order!
Text:
text body here
HTML:
html body here
--------------------------------------------------------------------------------
The Inbox (Rails only)
You can also preview sent emails using the inbox. Set the provider to inbox
and mount the engine: mount Courrier::Engine => "/courrier"
.
If you prefer to view your emails “in your face”, you can set config.inbox.auto_open = true
. 👊 This will open a preview of the email in your default browser when it gets delivered (just like the good-old letter_opener gem ❤️).
In Rails apps, the delivered emails for the inbox provider, are stored in tmp/courrier/emails. This folder is cleared via a hook to bin/rails tmp:clear
.
Layouts
The current set up of Courrier gently nudges you to keep emails simple and to-the-point. This is on purpose (or rather a nice side-effect). From my experience building, successful SaaS apps, basic emails tend to convert way better than complex HTML emails.
But if you need to prepend or append some text or HTML, you can do so using layouts. They work like this:
class OrderEmail < Courrier::Email
layout text: "%{content}\n\nThanks for your order!",
html: "<div>\n%{content}\n</div>"
end
Here the plain text emails are appended with Thanks for your order!
and the HTML content is wrapped with a <div></div>
. Next to just strings, you can also pass a method as a symbol (layout html: :html_layout
) or a callable class (layout html: OrderLayout
).
Minimal Email Editor
Speaking of crafting emails: along with the Courrier gem, I built a minimal HTML Email Editor. Write your text in Markdown and you can copy a HTML and plain text version. Ready to paste into your Courrier email classes. 💪
There are even more features, like: Auto-generate Text from HTML and Email Address Helper. 🚀
The foundation of Courrier is really solid, but there are still a few small and bigger features I have noted down (emails being scoped like this might give something away already 🤓).
For now it would be awesome if you could help validate the currently supported providers (most are already done). See this GitHub issue for more.
Also don’t forget to star it on GitHub before you give it a try. Looking forward to see your enhancements and the bugs you will find. 🎷🐛