Angular Email Builder
npm install @keycloakify/angular-emailAngular Email is a library that enables email template development using Angular, inspired by jsx-email. It provides a way to create email components using Angular's templating system.
To install Angular Email, use npm or yarn:
``sh`
npm install @keycloakify/angular-emailor
yarn add @keycloakify/angular-email
| Angular | Tailwind CSS | @keycloakify/angular-email | Maintained |
| ------- | ------------ | -------------------------- | ------------- |
| 20 | 4.x+ | 1.x+ | Yes |
| 20 | 3.x+ | 0.x+ | Bugfixes only |
see this example
To render the email as HTML or plaintext, use Angular's rendering engine:
`typescript
// email.component.ts
...
import { render, RenderToHtml } from '@keycloakify/angular-email';
...
export class EmailComponent {
....
}
type EmailComponentProps = {};
export const renderToHtml: RenderToHtml
return render({
component: EmailComponent,
selector: 'app-root',
props,
options: {
pretty: true,
},
});
};
`
`shcmd
export EMAIL_COMPONENTS_DIR_PATH="src/emails"
export EMAIL_OUTPUT_DIR_PATH="dist/emails"
export EMAIL_EXTERNAL_PACKAGES="tailwindcss,@tailwindcss/postcss,postcss,postcss-calc,postcss-custom-properties,postcss-preset-env,postcss-logical"
npx keycloakify-angular-email build -p "$EMAIL_COMPONENTS_DIR_PATH" -o "$EMAIL_OUTPUT_DIR_PATH" -e "$EMAIL_EXTERNAL_PACKAGES"
`
NB: use keycloakify-angular-email build when you don't need to pass dynamic inputs to your components, otherwise see Standalone Dynamic Rendering
see Email POC for a full example
`json
// emails/tsconfig.json
{
"extends": "../tsconfig.json",
"compileOnSave": false,
"compilerOptions": {
"outDir": "../out-tsc/emails",
"types": []
},
"files": [],
"include": [".ts", "/.ts"]
}
`
`ts
// email.component.ts
...
import { render } from '@keycloakify/angular-email';
import type { GetSubject, GetTemplate } from 'keycloakify-emails';
...
export class EmailComponent {
....
}
export const getTemplate: GetTemplate = async (props) => {
return await render({
component: EmailComponent,
props,
selector: 'kc-email-test',
options: {
signalInputsPrefix: '$',
pretty: true,
plainText: props.plainText,
},
});
};
export const getSubject: GetSubject = async (_props) => {
return '[KEYCLOAK] - SMTP test message';
};
`
`ts
// vite.config.ts
...
import { buildEmailTheme } from 'keycloakify-emails';
import { angularEsbuildPlugin } from '@keycloakify/angular-email/esbuild';
export default defineConfig(({ mode }) => ({
...
plugins: [
angular(),
keycloakify({
...
postBuild: async (buildContext) => {
await buildEmailTheme({
templatesSrcDirPath: join(import.meta.dirname, 'emails', 'templates'),
filterTemplate: (filePath: string) => !!filePath.endsWith('.component.ts'),
themeNames: buildContext.themeNames,
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath,
locales: ['en'],
cwd: import.meta.dirname,
i18nSourceFile: join(import.meta.dirname, 'emails', 'i18n.ts'),
environmentVariables: buildContext.environmentVariables,
assetsDirPath: join(import.meta.dirname, 'emails', 'templates', 'assets'),
esbuild: {
packages: 'bundle',
external: ['juice', '...other packages you might use to process css'],
format: 'esm',
outExtension: { '.js': '.mjs' },
plugins: [angularEsbuildPlugin(join(import.meta.dirname, 'emails'))],
},
});
},
}),
],
}));
`
Use it in a server environment
`js
// index.mjs
import { toHTML } from '@keycloakify/angular-email/node';
toHTML({
filePath: 'path/to/your.component.ts',
props: { foo: 'bar' },
externals: [],
})
.then((html) => {
console.log(html);
})
.catch((e) => {
console.error(e);
});
`
or
`js
// index.cjs
const { toHTML } = require('@keycloakify/angular-email/node');
toHTML({
filePath: 'path/to/your.component.ts',
props: { foo: 'bar' },
externals: [],
})
.then((html) => {
console.log(html);
})
.catch((e) => {
console.error(e);
});
`
`shcmd
node index.mjs # or node index.cjs
`
`ts`
type Render> = {
component: Type
/* Component selector /
selector: string;
/* Component inputs /
props?: Input;
options?: {
/* render as text /
plainText?: boolean;
/* format the html output /
pretty?: boolean;
/* Optional hook to manipulate the extracted CSS. Useful for PostCSS processing /
cssProcessor?: (css: string, html: string) => Promise
/* if you use prefix conventions on signal inputs /
signalInputsPrefix?: string;
};
};
`ts`
render>({ component, selector, props, options }: Render) => Promise
`ts`
angularEsbuildPlugin(cwd: string) => Plugin
`ts`
toHTML>(options: {
filePath: string;
props?: Input;
root?: string;
externals?: string[];
}) => Promise
Just a tailwind v4 preset, inspired by @maizzle/tailwindcss
`css`
/ styles.css /
@import '@keycloakify/angular-email/tailwindcss-preset-email';
`typescript
// email.component.ts
...
import { Component, ViewEncapsulation } from '@angular/core';
import { render, RenderToHtml } from '@keycloakify/angular-email';
// or your custom css processor implementation
import { cssProcessor } from '@keycloakify/angular-email/tailwindcss-preset-email/css-processor';
...
@Component({
...
styleUrls: ['styles.css'],
encapsulation: ViewEncapsulation.None,
})
export class EmailComponent {
....
}
type EmailComponentProps = {};
export const renderToHtml: RenderToHtml
return render({
component: EmailComponent,
selector: 'app-root',
props,
options: {
pretty: true,
cssProcessor,
},
});
};
``
Contributions are welcome! Feel free to open an issue or submit a pull request on GitHub.
This project is licensed under the MIT License. See the LICENSE file for details.
- Inspired by jsx-email
- Developed by Luca Peruzzo