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

vintasend-react-email

v0.13.3

Published

VintaSend template renderer implementation for React Email

Downloads

2,088

Readme

vintasend-react-email

React Email template renderer for VintaSend.

This package provides a BaseEmailTemplateRenderer implementation that can:

  • load templates from file paths (.js, .mjs, .cjs, .ts, .tsx, .mts, .cts)
  • compile uncompiled TypeScript/TSX templates at runtime
  • render React body templates to HTML via @react-email/render
  • render templates from inline content (including loops, conditionals, and arbitrary logic)

Installation

npm install vintasend-react-email

Exports

  • ReactEmailTemplateRenderer
  • ReactEmailTemplateRendererFactory
  • ReactEmailInlineTemplateRenderer
  • ReactEmailInlineTemplateRendererFactory

Template compilation script

For environments that can only deploy a single source file (e.g. Medplum bots), you can pre-compile template source files into a JSON map and then feed that map to an inline renderer.

This package provides a CLI:

compile-react-email-templates [input-directory] [output-file]

Supported template extensions:

  • .ts, .tsx, .js, .jsx, .mts, .cts, .mjs, .cjs

Add to package.json

{
   "scripts": {
      "compile-templates": "compile-react-email-templates ./notification-templates ./compiled-react-email-templates.json"
   }
}

Run

npm run compile-templates

The output JSON uses template paths (relative to input-directory) as keys and raw template source as values.

How it works

File-based templates (render)

When VintaSend sends a notification, the renderer reads:

  • notification.bodyTemplate
  • notification.subjectTemplate

Each file must export a function as either:

  • default export: export default function Template(context) { ... }
  • named export: export function render(context) { ... }

The function receives the notification context and returns:

  • for subject: usually a string
  • for body: a React element or a string

If body returns a React element, it is converted to HTML.

Inline templates (renderFromTemplateContent)

Inline templateContent.subject and templateContent.body are compiled and executed at runtime. You can pass either:

  1. Full module source (with export default or export function render), or
  2. Inline snippet:
    • function body (const x = ...; if (...) ...; return ...;)
    • or expression (<div>Hello</div>, 'Subject text')

This allows logic such as if/else, loops, mapping arrays, etc.

Usage

1) Create renderer instance

import { ReactEmailTemplateRendererFactory } from 'vintasend-react-email';

const templateRenderer = new ReactEmailTemplateRendererFactory<MyConfig>().create();

1.1) Create inline renderer instance (for pre-compiled templates)

Use this when your runtime cannot read template files directly and you want to load templates from a JSON map.

import compiledTemplates from './compiled-react-email-templates.json';
import { ReactEmailInlineTemplateRendererFactory } from 'vintasend-react-email';

const inlineTemplateRenderer = new ReactEmailInlineTemplateRendererFactory<MyConfig>().create(
   compiledTemplates,
);

Expected map shape:

type CompiledTemplates = Record<string, string>;

Where keys match the values you pass in notification.bodyTemplate and notification.subjectTemplate.

2) Use with your adapter (example)

// Example with an adapter that accepts a BaseEmailTemplateRenderer implementation
const adapter = new SomeEmailAdapterFactory<MyConfig>().create(templateRenderer);

Inline renderer with adapter:

const adapter = new SomeEmailAdapterFactory<MyConfig>().create(inlineTemplateRenderer);

3) Template files

subject-template.ts:

export default function SubjectTemplate(context: { name?: string }) {
   return `Welcome ${context.name ?? ''}`;
}

body-template.tsx:

import React from 'react';

export default function BodyTemplate(context: {
   name?: string;
   items?: string[];
   isVip?: boolean;
}) {
   const items = context.items ?? [];

   return (
      <div>
         <h1>Hello {context.name ?? ''}</h1>
         {context.isVip ? <p>VIP user</p> : <p>Standard user</p>}
         <ul>
            {items.map((item, index) => (
               <li key={index}>{item}</li>
            ))}
         </ul>
      </div>
   );
}

Inline content examples

Full module source

const templateContent = {
   subject: `
export default function Subject(context) {
   return ` + '`' + `Welcome ${context.name ?? ''}` + '`' + `;
}
   `,
   body: `
import React from 'react';

export default function Body(context) {
   return <p>Hello {context.name ?? ''}</p>;
}
   `,
};

Snippet (function body)

const templateContent = {
   subject: `
const tier = context.isVip ? 'VIP' : 'User';
return ` + '`' + `Welcome ${tier} ${context.name ?? ''}` + '`' + `;
   `,
   body: `
const items = Array.isArray(context.items) ? context.items : [];
return (
   <ul>
      {items.map((item, index) => (
         <li key={index}>{String(item)}</li>
      ))}
   </ul>
);
   `,
};

Validation behavior

  • throws if subjectTemplate (file-based rendering) is missing
  • throws if templateContent.subject (inline rendering) is missing
  • throws if template file/content does not export a function

Development

npm test
npm run build