hermes

package module
v0.0.0-...-2270742 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

Hermes

Build Status Go Report Card PkgGoDev

Fork: Maintained fork of matcornic/hermes with optional greeting, signature, and unsubscribe controls. Install this fork with go get github.com/chauhannishith/hermes.

Hermes is the Go port of the great mailgen engine for Node.js. Check their work, it's awesome! It's a package that generates clean, responsive HTML e-mails for sending transactional e-mails (welcome e-mails, reset password e-mails, receipt e-mails and so on), and associated plain text fallback.

Demo

Usage

First install the package:

go get github.com/chauhannishith/hermes

Migrate from upstream matcornic/hermes

This fork uses module path github.com/chauhannishith/hermes. If you were on the original package:

go get github.com/chauhannishith/hermes@v1.0.0

Replace imports from github.com/matcornic/hermes to github.com/chauhannishith/hermes in your code.

If you still use the upstream v2 module path (github.com/matcornic/hermes/v2), see upstream migration notes.

Use Hermes

Then, start using the package by importing and configuring it:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default) 
    Product: hermes.Product{
        // Appears in header & footer of e-mails
        Name: "Hermes",
        Link: "https://example-hermes.com/",
        // Optional product logo
        Logo: "http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png",
    },
}

Next, generate an e-mail using the following code:

email := hermes.Email{
    Body: hermes.Body{
        Name: "Jon Snow",
        Intros: []string{
            "Welcome to Hermes! We're very excited to have you on board.",
        },
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please click here:",
                Button: hermes.Button{
                    Color: "#22BC66", // Optional action button color
                    Text:  "Confirm your account",
                    Link:  "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
                },
            },
        },
        Outros: []string{
            "Need help, or have questions? Just reply to this email, we'd love to help.",
        },
    },
}

// Generate an HTML email with the provided contents (for modern clients)
emailBody, err := h.GenerateHTML(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

// Generate the plaintext version of the e-mail (for clients that do not support xHTML)
emailText, err := h.GeneratePlainText(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

// Optionally, preview the generated HTML e-mail by writing it to a local file
err = os.WriteFile("preview.html", []byte(emailBody), 0644)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

This code would output the following HTML template:

And the following plain text:


------------
Hi Jon Snow,
------------

Welcome to Hermes! We're very excited to have you on board.

To get started with Hermes, please click here: https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010

Need help, or have questions? Just reply to this email, we'd love to help.

Yours truly,
Hermes - https://example-hermes.com/

Copyright © 2025 Hermes. All rights reserved.

Theme templates are embedded in your application binary. To use your own layout or load templates from files, implement a custom theme.

More Examples

To run the examples, go to examples folder, then run go run .. HTML and plaintext examples are created in examples/default/.

Optionaly you can set the following variables to send automatically the emails to one your mailbox. Nice for testing template in real email clients.

  • HERMES_SEND_EMAILS=true
  • HERMES_SMTP_SERVER=<smtp_server> : for Gmail it's smtp.gmail.com
  • HERMES_SMTP_PORT=<smtp_port> : for Gmail it's 465
  • HERMES_SENDER_EMAIL=<your_sender_email>
  • HERMES_SENDER_IDENTITY=<the sender name>
  • HERMES_SMTP_USER=<smtp user> : usually the same than HERMES_SENDER_EMAIL
  • HERMES_TO=<recipients emails>: split by commas like myadress@test.com,somethingelse@gmail.com

The program will ask for your SMTP password. If needed, you can set it with HERMES_SMTP_PASSWORD variable (but be careful where you put this information !)

Plaintext E-mails

To generate a plaintext version of the e-mail, simply call GeneratePlainText function:

// Generate plaintext email using hermes
emailText, err := h.GeneratePlainText(email)
if err != nil {
    panic(err) // Tip: Handle error with something else than a panic ;)
}

Supported Themes

The following open-source themes are bundled with this package:

Custom themes

Only the default theme ships with this package. To change the layout (not just colors or fonts), implement hermes.Theme and pass it when creating Hermes:

h := hermes.Hermes{
    Theme: new(MyCustomTheme), // see default.go for the interface
    Product: hermes.Product{
        Name: "Acme",
        Link: "https://example.com/",
    },
}

Your theme must provide HTML and plain-text templates plus base styles. Templates receive Hermes, Email, and StylesCSS — use the injection snippets so Body fields (intros, actions, table, etc.) render correctly.

Full guide: Using a Custom Theme in CONTRIBUTING.md (interface methods, template snippets, built-in theme checklist).

For styling only on the default layout, use CustomCSS instead — no custom theme required.

RTL Support

To change the default text direction (left-to-right), simply override it as follows:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes {
    // Custom text direction
    TextDirection: hermes.TDRightToLeft,
}

Language Customizations

Set a default greeting for all emails on Hermes, or override it per email on Body. The default greeting is Hi.

h := hermes.Hermes{
    DefaultGreeting: "Dear",
    Product: hermes.Product{
        Name: "Acme",
        Link: "https://example.com/",
    },
}

Per-email overrides:

email := hermes.Email{
    Body: hermes.Body{
        Greeting:  "Hello",
        Signature: "Sincerely",
    },
}

To omit the greeting from the title line, set DisableGreeting to true. The recipient name still appears unless Title is set:

email := hermes.Email{
    Body: hermes.Body{
        DisableGreeting: true,
    },
}

To omit the signature line entirely, set DisableSignature to true. The product name still appears in the closing block:

email := hermes.Email{
    Body: hermes.Body{
        DisableSignature: true,
    },
}

To add an optional unsubscribe link in the email footer, set UnsubscribeLink on Body. Use UnsubscribeText to customize the link label (defaults to Unsubscribe):

email := hermes.Email{
    Body: hermes.Body{
        UnsubscribeLink: "https://example.com/unsubscribe?token=abc",
        UnsubscribeText: "Manage email preferences", // optional
    },
}

To use a custom title string rather than a greeting/name introduction, provide it instead of Name:

email := hermes.Email{
    Body: hermes.Body{
        // Title will override `Name`
        Title: "Welcome to Hermes",
    },
}

To customize the Copyright, override it when initializing Hermes within your Product as follows. If omitted, the default copyright uses the current year automatically:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default)
    Product: hermes.Product{
        // Appears in header & footer of e-mails
        Name: "Hermes",
        Link: "https://example-hermes.com/",
        // Custom copyright notice (year is auto-filled in the default when Copyright is omitted)
        Copyright: "Copyright © 2025 Dharma Initiative. All rights reserved."
    },
}

To use a custom fallback text at the end of the email, change the TroubleText field of the hermes.Product struct. The default value is If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.. The {ACTION} placeholder will be replaced with the corresponding text of the supplied action button:

// Configure hermes by setting a theme and your product info
h := hermes.Hermes{
    // Optional Theme
    // Theme: new(Default)
    Product: hermes.Product{
        // Custom trouble text
        TroubleText: "If the {ACTION}-button is not working for you, just copy and paste the URL below into your web browser."
    },
}

Since v2.1.0, Hermes is automatically inlining all CSS to improve compatibility with email clients, thanks to Premailer. You can disable this feature by setting DisableCSSInlining of Hermes struct to true.

h := hermes.Hermes{
    ...
    DisableCSSInlining: true,
}
Custom CSS overrides

Hermes keeps a fixed email layout (header, body sections, footer). You customize appearance with CSS; content is set only through the existing API — Hermes.Product, Body.Name, Body.Intros, Body.Actions, and the other fields documented below. There is no per-email HTML template override.

Set CustomCSS on Body to load a stylesheet from a file path, URL, or as inline CSS. Only selectors you define are merged on top of the default theme — the structure and which values appear stay the same.

email := hermes.Email{
    Body: hermes.Body{
        Name:      "Jon Snow",
        Intros:    []string{"Welcome!"},
        CustomCSS: "brand.css", // restyle .button, body, h1, etc.
    },
}

Use the class names from the default theme (e.g. .button, .email-body, .content-cell) in your CSS file. See examples/custom.css.

For a completely different layout, see Custom themes or CONTRIBUTING.md — Using a Custom Theme.

Test that overrides work

# Unit/integration tests (merge logic + example file)
go test ./... -run CustomCSS

# Generate preview HTML under examples/custom/ (from repo root)
go test ./examples/ -run TestGenerateCustomCSSExamples

Open examples/custom/custom.welcome.html in a browser and compare with examples/default/default.welcome.html.

go run . in examples/ still generates only the default theme — custom previews are produced by the test above.

Elements

Hermes supports injecting custom elements such as dictionaries, tables and action buttons into e-mails.

Action

To inject an action button in to the e-mail, supply the Actions object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please click here:",
                Button: hermes.Button{
                    Color: "#22BC66", // Optional action button color
                    Text:  "Confirm your account",
                    Link:  "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
                },
            },
        },
    },
}

Alternatively, instead of having a button, an action can be an invite code as follows:

email := hermes.Email{
    Body: hermes.Body{
        Actions: []hermes.Action{
            {
                Instructions: "To get started with Hermes, please use the invite code:",
                InviteCode: "123456",
            },
        },
    },
}

To inject multiple action buttons in to the e-mail, supply another struct in Actions slice Action.

Table

To inject a table into the e-mail, supply the Table object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Table: hermes.Table{
            Data: [][]hermes.Entry{
                // List of rows
                {   
                    // Key is the column name, Value is the cell value
                    // First object defines what columns will be displayed
                    {Key: "Item", Value: "Golang"},
                    {Key: "Description", Value: "Open source programming language that makes it easy to build simple, reliable, and efficient software"},
                    {Key: "Price", Value: "$10.99"},
                },
                {
                    {Key: "Item", Value: "Hermes"},
                    {Key: "Description", Value: "Programmatically create beautiful e-mails using Golang."},
                    {Key: "Price", Value: "$1.99"},
                },
            },
            Columns: hermes.Columns{
                // Custom style for each rows
                CustomWidth: map[string]string{
                    "Item":  "20%",
                    "Price": "15%",
                },
                CustomAlignment: map[string]string{
                    "Price": "right",
                },
            },
        },
    },
}
Dictionary

To inject key-value pairs of data into the e-mail, supply the Dictionary object as follows:

email := hermes.Email{
    Body: hermes.Body{
        Dictionary: []hermes.Entry{
            {Key: "Date", Value: "20 November 1887"},
            {Key: "Address", Value: "221B Baker Street, London"},
        },
    },
}
Free Markdown

If you need more flexibility in the content of your generated e-mail, while keeping the same format than any other e-mail, use Markdown content. Supply the FreeMarkdown object as follows:

email := hermes.Email{
		Body: hermes.Body{
			FreeMarkdown: `
> _Hermes_ service will shutdown the **1st August 2025** for maintenance operations. 

Services will be unavailable based on the following schedule:

| Services | Downtime |
| :------:| :-----------: |
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |

---

Feel free to contact us for any question regarding this matter at [support@hermes-example.com](mailto:support@hermes-example.com) or in our [Gitter](https://gitter.im/)

`,
		},
	}
}

This code would output the following HTML template:

And the following plaintext:

------------
Hi Jon Snow,
------------

> 
> 
> 
> Hermes service will shutdown the *1st August 2025* for maintenance
> operations.
> 
> 

Services will be unavailable based on the following schedule:

+-----------+------------+
| SERVICES  |  DOWNTIME  |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+

Feel free to contact us for any question regarding this matter at support@hermes-example.com ( support@hermes-example.com ) or in our Gitter ( https://gitter.im/ )

Yours truly,
Hermes - https://example-hermes.com/

Copyright © 2025 Hermes. All rights reserved.

Be aware that this content will replace existing tables, dictionary and actions. Only intros, outros, header and footer will be kept.

This is helpful when your application needs sending e-mails, wrote on-the-fly by adminstrators.

Markdown is rendered with Blackfriday, so every thing Blackfriday can do, Hermes can do it as well.

Troubleshooting

  1. After sending multiple e-mails to the same Gmail / Inbox address, they become grouped and truncated since they contain similar text, breaking the responsive e-mail layout.

Simply sending the X-Entity-Ref-ID header with your e-mails will prevent grouping / truncation.

Contributing

See CONTRIBUTING.md. To add your own email layout, start with Using a Custom Theme.

Continuous integration

This fork runs GitHub Actions on every push and pull request (build, test, and lint). If workflows do not run after forking, enable them under Settings → Actions → General on your GitHub fork.

License

Apache 2.0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action struct {
	Instructions string
	Button       Button
	InviteCode   string
}

Action is anything the user can act on (i.e., click on a button, view an invite code)

type Body

type Body struct {
	Name             string   // The name of the contacted person
	Intros           []string // Intro sentences, first displayed in the email
	Dictionary       []Entry  // A list of key+value (useful for displaying parameters/settings/personal info)
	Table            Table    // Table is an table where you can put data (pricing grid, a bill, and so on)
	Actions          []Action // Actions are a list of actions that the user will be able to execute via a button click
	Outros           []string // Outro sentences, last displayed in the email
	Greeting         string   // Greeting for the contacted person (default to 'Hi')
	DisableGreeting  bool     // When true, omit the greeting from the email title
	Signature        string   // Signature for the contacted person (default to 'Yours truly')
	DisableSignature bool     // When true, omit the signature from the email body
	Title            string   // Title replaces the greeting+name when set
	FreeMarkdown     Markdown // Free markdown content that replaces all content other than header and footer
	UnsubscribeLink  string   // Optional URL for an unsubscribe link in the email footer
	UnsubscribeText  string   // Optional text for the unsubscribe link (defaults to 'Unsubscribe')
	CustomCSS        string   // CSS overrides: file path, URL, or inline. Restyles default layout; content uses Hermes, Product, and Body fields only.
}

Body is the body of the email, containing all interesting data

type Button

type Button struct {
	Color     string
	TextColor string
	Text      string
	Link      string
}

Button defines an action to launch

type Columns

type Columns struct {
	CustomWidth     map[string]string
	CustomAlignment map[string]string
}

Columns contains meta-data for the different columns

type Default

type Default struct{}

Default is the theme by default

func (*Default) HTMLTemplate

func (dt *Default) HTMLTemplate() string

HTMLTemplate returns a Golang template that will generate an HTML email.

func (*Default) Name

func (dt *Default) Name() string

Name returns the name of the default theme

func (*Default) PlainTextTemplate

func (dt *Default) PlainTextTemplate() string

PlainTextTemplate returns a Golang template that will generate an plain text email.

func (*Default) Styles

func (dt *Default) Styles() StylesDefinition

type Email

type Email struct {
	Body Body
}

Email is the email containing a body

type Entry

type Entry struct {
	Key   string
	Value string
}

Entry is a simple entry of a map Allows using a slice of entries instead of a map Because Golang maps are not ordered

type Hermes

type Hermes struct {
	Theme              Theme
	TextDirection      TextDirection
	Product            Product
	DefaultGreeting    string // Default greeting for emails (defaults to 'Hi')
	DisableCSSInlining bool
}

Hermes is an instance of the hermes email generator

func (*Hermes) GenerateHTML

func (h *Hermes) GenerateHTML(email Email) (string, error)

GenerateHTML generates the email body from data to an HTML Reader This is for modern email clients

func (*Hermes) GeneratePlainText

func (h *Hermes) GeneratePlainText(email Email) (string, error)

GeneratePlainText generates the email body from data This is for old email clients

type Markdown

type Markdown template.HTML

Markdown is a HTML template (a string) representing Markdown content https://en.wikipedia.org/wiki/Markdown

func (Markdown) ToHTML

func (c Markdown) ToHTML() template.HTML

ToHTML converts Markdown to HTML

type Product

type Product struct {
	Name      string
	Link      string // e.g. https://matcornic.github.io
	Copyright string // Copyright © 2019 Hermes. All rights reserved.
	// TroubleText is the sentence at the end of the email for users having trouble with the button
	// (default to `If you’re having trouble with the button '{ACTION}',
	// copy and paste the URL below into your web browser.`)
	TroubleText string
}

Product represents your company product (brand) Appears in header & footer of e-mails

type StylesDefinition

type StylesDefinition map[string]map[string]any

func GetDefaultStyles

func GetDefaultStyles() StylesDefinition

GetDefaultStyles returns the parsed CSS styles for the default theme.

func ParseStylesDefinition

func ParseStylesDefinition(css string) StylesDefinition

ParseStylesDefinition parses a raw CSS string into a StylesDefinition. It supports a very small subset of CSS adequate for simple selector { prop: value; } rules.

type Table

type Table struct {
	Data    [][]Entry // Contains data
	Columns Columns   // Contains meta-data for display purpose (width, alignement)
}

Table is an table where you can put data (pricing grid, a bill, and so on)

type Template

type Template struct {
	Hermes    Hermes
	Email     Email
	StylesCSS template.CSS
}

Template is the struct given to Golang templating Root object in a template is this struct

type TextDirection

type TextDirection string

TextDirection of the text in HTML email

const TDLeftToRight TextDirection = "ltr"

TDLeftToRight is the text direction from left to right (default)

const TDRightToLeft TextDirection = "rtl"

TDRightToLeft is the text direction from right to left

type Theme

type Theme interface {
	Name() string              // The name of the theme
	Styles() StylesDefinition  // The CSS styles definition for the theme
	HTMLTemplate() string      // The golang template for HTML emails
	PlainTextTemplate() string // The golang templte for plain text emails (can be basic HTML)
}

Theme is an interface to implement when creating a new theme

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL