Getting Started with Bamboo
This guide walks you through installing Bamboo, setting up a mailer, and sending your first email.
Installation
Add bamboo to your dependencies in mix.exs:
defp deps do
[
{:bamboo, "~> 2.3.0"},
{:jason, "~> 1.0"}
]
end
Then fetch dependencies:
mix deps.get
Bamboo uses Jason for JSON encoding by default. If you prefer a different JSON library, you can configure it:
config :bamboo, :json_library, SomeOtherLib
Define Your Mailer
Create a mailer module that Bamboo will use to send emails. This module ties your application to an adapter through configuration:
defmodule MyApp.Mailer do
use Bamboo.Mailer, otp_app: :my_app
end
Configure the Adapter
In your config files, tell the mailer which adapter to use. For development, the LocalAdapter stores emails in memory so you can inspect them without sending anything externally:
# config/dev.exs
config :my_app, MyApp.Mailer,
adapter: Bamboo.LocalAdapter
For production, configure a real adapter like SendGrid:
# config/prod.exs
config :my_app, MyApp.Mailer,
adapter: Bamboo.SendGridAdapter,
api_key: System.get_env("SENDGRID_API_KEY")
See the Adapters guide for a full list of available adapters and their configuration.
Create an Email Module
Define a module where you build your emails. Import Bamboo.Email to get access to all the composition functions:
defmodule MyApp.Email do
import Bamboo.Email
def welcome_email(user) do
new_email(
to: user.email,
from: "support@myapp.com",
subject: "Welcome to MyApp!",
html_body: "<h1>Welcome, #{user.name}!</h1><p>Thanks for joining.</p>",
text_body: "Welcome, #{user.name}! Thanks for joining."
)
end
end
Pipe-Based Composition
You can also build emails incrementally using pipes, which is useful when you want to set shared defaults:
defmodule MyApp.Email do
import Bamboo.Email
def welcome_email(user) do
base_email()
|> to(user.email)
|> subject("Welcome to MyApp!")
|> html_body("<h1>Welcome, #{user.name}!</h1>")
|> text_body("Welcome, #{user.name}!")
end
def password_reset_email(user, token) do
base_email()
|> to(user.email)
|> subject("Reset your password")
|> html_body("<p>Click <a href=\"https://myapp.com/reset/#{token}\">here</a> to reset.</p>")
|> text_body("Visit https://myapp.com/reset/#{token} to reset your password.")
end
defp base_email do
new_email()
|> from("support@myapp.com")
|> put_header("Reply-To", "help@myapp.com")
end
end
Send an Email
Use your mailer module to deliver emails. Bamboo provides four delivery functions:
# Synchronous — returns {:ok, email} or {:error, error}
MyApp.Email.welcome_email(user) |> MyApp.Mailer.deliver_now()
# Synchronous — raises on failure
MyApp.Email.welcome_email(user) |> MyApp.Mailer.deliver_now!()
# Background — returns {:ok, email} or {:error, error}
MyApp.Email.welcome_email(user) |> MyApp.Mailer.deliver_later()
# Background — raises on failure
MyApp.Email.welcome_email(user) |> MyApp.Mailer.deliver_later!()
deliver_now sends the email immediately and blocks until the adapter responds. deliver_later hands the email off to a background process using Bamboo’s TaskSupervisorStrategy by default.
For most production use cases, prefer deliver_later to avoid blocking your request handling.
View Sent Emails in Development
When using the LocalAdapter, you can add Bamboo’s built-in email viewer to your Phoenix router to inspect sent emails in the browser:
# lib/my_app_web/router.ex
if Mix.env() == :dev do
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
Visit /sent_emails in your browser to see all emails sent during the current session. This is especially handy for checking password reset links, confirmation emails, and other transactional messages without connecting to an external service.
Recipients
Bamboo accepts several formats for recipient addresses:
# Plain string
new_email(to: "user@example.com")
# Name and address tuple
new_email(to: {"Jane Doe", "jane@example.com"})
# Lists for multiple recipients
new_email(to: ["one@example.com", {"Two", "two@example.com"}])
You can also use cc/2 and bcc/2:
new_email()
|> to("primary@example.com")
|> cc("team@example.com")
|> bcc(["logs@example.com", "archive@example.com"])
For custom structs (like a %User{}), implement the Bamboo.Formatter protocol so you can pass them directly to to/2, from/2, and other recipient functions.
Attachments
Add file attachments to any email:
new_email()
|> to("user@example.com")
|> from("app@example.com")
|> subject("Your report")
|> text_body("See attached.")
|> put_attachment("path/to/report.pdf")
Next Steps
- Adapters — Configure adapters for SendGrid, Mailgun, Mandrill, and more
- Testing — Test email delivery in your application
- Cheatsheet — Quick reference for all functions