npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codee_team/medusa-notification-templates

v1.0.1

Published

A collection of ready-to-use email templates using MJML and a reusable component system

Readme

Notification Templates

A collection of ready-to-use email templates using MJML and a reusable component system.

Table of Contents

Features

  • Theme System - Centralized configuration for colors, fonts, and styles
  • Reusable Components - Ready-to-use sections for building emails
  • Responsive Design - All templates are responsive thanks to MJML
  • TypeScript Support - Full TypeScript support with type definitions
  • Internationalization - Built-in support for multiple languages with per-template translations
  • Easy Extension - Simple way to add new templates
  • Unified API - Single renderTemplate function for all templates

Installation

Install from NPM

npm install @codee_team/medusa-notification-templates

Requirements

The package includes mjml as a dependency, so you don't need to install it separately.

Usage in Medusa.js Project

After installation, import templates where you send notifications:

import { renderTemplate } from "@codee_team/medusa-notification-templates";

Legacy Installation (Copy Method)

If you prefer to copy the templates folder directly:

  1. Copy the templates folder to your notification module
  2. Install dependencies:
    npm install mjml
    npm install --save-dev @types/mjml
  3. Import templates:
    import { renderTemplate } from "./templates/emails";

Quick Start

Basic Usage with Unified API

import { renderTemplate, ContactFormTemplateData } from "@codee_team/medusa-notification-templates";

const templateName = "contact-form";

const data: ContactFormTemplateData = {
  subject: "New contact form message",
  name: "John Doe",
  email: "[email protected]",
  phone: "+1 234 567 890",
  message: "Hello, I would like to..."
};

const { html, text } = renderTemplate(templateName, data, { locale: "pl" });

// Use with Medusa notification service
await notificationService.createNotifications({
  to: "[email protected]",
  channel: "email",
  template: templateName,
  data: {
    html,
    text,
    subject: data.subject,
  },
});

Available Components

All components are available from shared/components:

import {
  headerSection,
  footerSection,
  textSection,
  dividerSection,
  buttonSection,
  richTextSection,
  Theme,
} from "@codee_team/medusa-notification-templates";

headerSection(text, options?)

Header section with colored background.

headerSection("Email Title", { theme })

footerSection(message, options?)

Footer section with message.

footerSection("Footer message", { theme })

textSection(label, value, options?)

Section with label and value (e.g., "Email: [email protected]").

textSection("Email", "[email protected]", { theme })

dividerSection(options?)

Horizontal separator.

dividerSection({ theme })

buttonSection(text, url, options?)

Call-to-action button.

buttonSection("View Order", "https://example.com/order/123", { 
  theme,
  align: "center" // "left" | "center" | "right"
})

richTextSection(content, options?)

Section with formatted HTML text.

richTextSection("This is <strong>important</strong> message", { 
  theme,
  align: "center"
})

Internationalization

Templates support multiple languages through the i18n system. Each template has its own translations in the locales/ folder. The default language is Polish (pl), but you can use English (en) or add more languages.

Using Different Languages

import { renderTemplate } from "@codee_team/medusa-notification-templates";

// Polish (default)
const { html: htmlPL } = renderTemplate("contact-form", data);

// English
const { html: htmlEN } = renderTemplate("contact-form", data, { locale: "en" });

Adding a New Language to a Template

  1. Create a new locale file in the template's locales/ folder (e.g., de.ts for German)
  2. Add translations following the structure in locales/types.ts
  3. Export it in locales/index.ts:
// locales/de.ts
import { ContactFormTranslations } from "./types";

export const de: ContactFormTranslations = {
  labels: {
    name: "Name",
    email: "E-Mail",
    phone: "Telefon",
    message: "Nachricht",
  },
  footer: "Diese Nachricht wurde automatisch vom Kontaktformular gesendet",
};
// locales/index.ts
import { ContactFormTranslations } from "./types";
import { Locale } from "../../../shared/i18n";
import { pl } from "./pl";
import { en } from "./en";
import { de } from "./de";

export const translations: Record<Locale, ContactFormTranslations> = {
  pl,
  en,
  de, // Add new language
};
  1. Update Locale type in shared/i18n/types.ts:
export type Locale = "pl" | "en" | "de";
  1. Use it: renderTemplate("contact-form", data, { locale: "de" })

Customizing Theme

You can customize colors, fonts, and other styles by modifying shared/theme/presets/default/index.ts or by passing a custom theme to template functions:

import { Theme } from "@codee_team/medusa-notification-templates";

const customTheme: Theme = {
  colors: {
    primary: "#FF5733",
    primaryText: "#ffffff",
    background: "#ffffff",
    surface: "#f5f5f5",
    text: {
      primary: "#000000",
      secondary: "#333333",
      muted: "#999999",
    },
    border: "#e0e8f0",
  },
  fonts: {
    primary: "Roboto",
    fallback: "sans-serif",
  },
  spacing: {
    section: "20px 20px 20px 20px",
    text: "0",
    divider: "0 30px",
  },
  typography: {
    header: {
      fontSize: "20px",
      fontWeight: "600",
      lineHeight: "24px",
    },
    label: {
      fontSize: "11px",
      fontWeight: "600",
      textTransform: "uppercase",
      letterSpacing: "1px",
    },
    body: {
      fontSize: "16px",
      lineHeight: "24px",
    },
    footer: {
      fontSize: "12px",
    },
  },
};

const { html } = renderTemplate("contact-form", data, { theme: customTheme });

Creating a New Template

Step 1: Create Template Folder

mkdir templates/emails/my-new-template
mkdir templates/emails/my-new-template/locales

Step 2: Define Data Types (types.ts)

export type MyNewTemplateData = {
  subject: string;
  userName: string;
  userEmail: string;
  customField?: string;
};

Step 3: Create Translations (locales/)

Create translation files for each language:

// locales/types.ts
export interface MyNewTemplateTranslations {
  greeting: string;
  labels: {
    userName: string;
    userEmail: string;
  };
  footer: string;
}
// locales/pl.ts
import { MyNewTemplateTranslations } from "./types";

export const pl: MyNewTemplateTranslations = {
  greeting: "Witaj",
  labels: {
    userName: "Nazwa użytkownika",
    userEmail: "Email",
  },
  footer: "Wiadomość automatyczna",
};
// locales/en.ts
import { MyNewTemplateTranslations } from "./types";

export const en: MyNewTemplateTranslations = {
  greeting: "Hello",
  labels: {
    userName: "User Name",
    userEmail: "Email",
  },
  footer: "Automated message",
};
// locales/index.ts
import { MyNewTemplateTranslations } from "./types";
import { Locale } from "../../../shared/i18n";
import { pl } from "./pl";
import { en } from "./en";

export const translations: Record<Locale, MyNewTemplateTranslations> = {
  pl,
  en,
};

export { type MyNewTemplateTranslations } from "./types";
export { type Locale } from "../../../shared/i18n";
export { pl, en };

Step 4: Create Main Logic (main.ts)

import {
  headerSection,
  footerSection,
  textSection,
  dividerSection,
  escapeHtml,
  Theme,
} from "../../shared/components";
import { getTranslations } from "../../shared/i18n";
import { translations, Locale } from "./locales";
import { MyNewTemplateData } from "./types";

interface MyNewTemplateMainOptions {
  theme?: Theme;
  locale?: Locale;
}

export function getMyNewTemplateMain(
  data: MyNewTemplateData,
  options: MyNewTemplateMainOptions = {}
): string {
  const theme = options.theme;
  const locale = options.locale || "pl";
  const t = getTranslations(translations, locale);

  return `
    ${headerSection(data.subject, { theme })}
    ${textSection(t.labels.userName, escapeHtml(data.userName), { theme })}
    ${dividerSection({ theme })}
    ${textSection(t.labels.userEmail, escapeHtml(data.userEmail), { theme })}
    ${footerSection(t.footer, { theme })}
  `.trim();
}

Step 5: Create Export (index.ts)

import mjml2html from "mjml";
import { escapeHtml, Theme } from "../../shared/components";
import { getTranslations } from "../../shared/i18n";
import { translations, Locale } from "./locales";
import { getMyNewTemplateMain } from "./main";
import { MyNewTemplateData } from "./types";

interface MyNewTemplateOptions {
  theme?: Theme;
  locale?: Locale;
}

export function getMyNewTemplateHtml(
  data: MyNewTemplateData,
  options: MyNewTemplateOptions = {}
): string {
  return mjml2html(
    `
    <mjml>
      <mj-head>
        <mj-title>${escapeHtml(data.subject)}</mj-title>
      </mj-head>
      <mj-body>
        ${getMyNewTemplateMain(data, options)}
      </mj-body>
    </mjml>
  `,
    {
      keepComments: false,
    }
  ).html;
}

export function getMyNewTemplateText(
  data: MyNewTemplateData,
  options: MyNewTemplateOptions = {}
): string {
  const locale = options.locale || "pl";
  const t = getTranslations(translations, locale);

  return `
${data.subject}

${t.labels.userName}: ${data.userName}
${t.labels.userEmail}: ${data.userEmail}

---
${t.footer}
  `.trim();
}

Step 6: Register Template in emails/index.ts

Add your template to the registry:

import { getMyNewTemplateHtml, getMyNewTemplateText } from "./my-new-template";
import { MyNewTemplateData } from "./my-new-template/types";

export type TemplateName = "contact-form" | "order-created" | "my-new-template";

export type TemplateData = ContactFormTemplateData | OrderCreatedTemplateData | MyNewTemplateData;

const templateRegistry: Record<TemplateName, TemplateRenderer> = {
  "contact-form": {
    getHtml: getContactFormHtml,
    getText: getContactFormText,
  },
  "order-created": {
    getHtml: getOrderCreatedHtml,
    getText: getOrderCreatedText,
  },
  "my-new-template": {
    getHtml: getMyNewTemplateHtml,
    getText: getMyNewTemplateText,
  },
};

// Add type-safe overload
export function renderTemplate(
  templateName: "my-new-template",
  data: MyNewTemplateData,
  options?: TemplateOptions
): { html: string; text: string };

Step 7: Use in Your Code

import { renderTemplate } from "@codee_team/medusa-notification-templates";

const { html, text } = renderTemplate("my-new-template", data, { locale: "pl" });

Examples

Example 1: Contact Form

See implementation in emails/contact-form/ - a simple form with text fields.

Example 2: Order Created

See implementation in emails/order-created/ - a more complex template with product list, CTA button, and shipping address.

Utility Functions

escapeHtml(text)

Escapes HTML in text to prevent XSS attacks. Available from shared/utils:

import { escapeHtml } from "@codee_team/medusa-notification-templates";

const safeText = escapeHtml("<script>alert('xss')</script>");
// Returns: "&lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt;"

Support

If you have questions or suggestions, please create an issue in the GitHub repository.