Skip to content
Email library for Crystal. Testable, adapter-based, and catches bugs for you. Comes with an adapter for SendGrid.
Crystal Shell HTML
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
spec Annotate return types of abstract methods Aug 2, 2019
src Bump to version 0.1.1 Aug 7, 2019
.gitignore Get code from lucky-auth sample app Apr 7, 2018
.travis.yml CI: Add format check tool and nightly Aug 2, 2019
LICENSE Initial commit Apr 6, 2018
shard.yml Bump to version 0.1.1 Aug 7, 2019


Email library written in Crystal.

code preview


Add this to your application's shard.yml:

    github: luckyframework/carbon



First, create a base class for your emails

require "carbon"

# You can setup defaults in this class
abstract class BaseEmail < Carbon::Email
  # For example, set up a default 'from' address
  from"My App Name", "")
  # Use a string if you just need the email address
  from ""

Configure the mailer class

BaseEmail.configure do
  settings.adapter = "SEND_GRID_API_KEY")

Create a class for your email

# Create an email class
class WelcomeEmail < BaseEmail
  def initialize(@name : String, @email_address : String)

  to @email_address
  subject "Welcome, #{@name}!"
  header "My-Custom-Header", "header-value"
  reply_to ""
  # You can also do just `text` or `html` if you don't want both
  templates text, html

Create templates

Templates go in the same folder the email is in:

  • Text email: <folder_email_class_is_in>/templates/<underscored_class_name>/text.ecr
  • HTML email: <folder_email_class_is_in>/templates/<underscored_class_name>/html.ecr

So if your email class is in src/my_app/emails/, then your templates would go in src/my_app/emails/welcome_email/text|html.ecr.

# in <folder_of_email_class>/templates/welcome_email/text.ecr
# Templates have access to instance variables and methods in the email.
Welcome, #{@name}!
# in <folder_of_email_class>/templates/welcome_email/html.ecr
<h1>Welcome, #{@name}!</h1>

Deliver the email

# Send the email right away!"Kate", "").deliver

# Send the email in the background using `spawn`"Kate", "").deliver_later


Change the adapter

# In spec/ or wherever you configure your code
BaseEmail.configure do
  # This adapter will capture all emails in memory
  settings.adapter =

Reset emails before each spec and include expectations

# In spec/

# This gives you the `be_delivered` expectation
include Carbon::Expectations

Spec.before_each do

Integration testing

# Let's say we have a class that signs the user up and sends the welcome email
# that was described at the beginning of the README
class SignUpUser
  def initialize(@name : String, @email_address : String)

  def run
    sign_user_up @name, email_address: @email_address).deliver_now

it "sends an email after the user signs up" do "Emily", email_address: "").run

  # Test that this email was sent "Emily", email_address: "").should be_delivered

Unit testing

Unit testing is simple. Instantiate your email and test the fields you care about.

it "builds a nice welcome email" do
  email = "David", email_address: "")
  # Note that recipients are converted to an array of Carbon::Address
  # So if you use a string value for the `to` field, you'll get an array of
  # Carbon::Address instead. eq ["")]
  email.text_body.should contain "Welcome"
  email.html_body.should contain "Welcome"

Note that unit testing can be superfluous in most cases. Instead, try unit testing just fields that have complex logic. The compiler will catch most other issues.


  • shards install
  • Make changes
  • crystal spec -D skip-integration (will skip sending test emails to SendGrid)
  • crystal spec requires a SEND_GRID_API_KEY ENV variable. Set this in a .env file:
# in .env
# If you want to run tests that actually test emails against the SendGrid server

Note: When you open a PR, Travis CI will run the test suite and try sending a sandboxed email through SendGrid. Feel free to open a PR to run integration tests if you don't want to get an API key from SendGrid.


  1. Fork it ( )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Make your changes
  4. Run ./bin/test to run the specs, build shards, and check formatting
  5. Commit your changes (git commit -am 'Add some feature')
  6. Push to the branch (git push origin my-new-feature)
  7. Create a new Pull Request


You can’t perform that action at this time.