@nomideusz/svelte-notify
v1.0.0
Published
Email notification template library for the @nomideusz booking platform.
Maintainers
Readme
@nomideusz/svelte-notify
Transactional email templates for booking products — confirmation with QR ticket, new-booking provider notification, cancellation with refund copy. Localized (en/pl/uk), brandable, and overridable per call so every app keeps its own voice.
Pairs with @nomideusz/svelte-scheduler and @nomideusz/svelte-payments; QR codes via @nomideusz/svelte-qr.
Installation
npm install @nomideusz/svelte-notifyUsage
Bind your app's identity once, then call templates with only what varies:
import { createNotifier } from '@nomideusz/svelte-notify';
export const notify = createNotifier({
brand: 'szkolyjogi.pl',
language: 'pl', // default; per-recipient wins
theme: { accent: '#1a7f4b', radius: '6px' },
overrides: (lang) => VOICE[lang], // house voice, per language
});
const { subject, html } = notify.bookingConfirmation({ ...booking });notify.actionLink covers the link-and-a-button emails every app needs on day
one — magic-link sign-in, password reset, address verification, team invite:
const { subject, html } = notify.actionLink({
subject: 'Twój link do logowania — szkolyjogi.pl',
headingHtml: 'Zaloguj się',
bodyHtml: '<p style="margin:0;">Link jest jednorazowy i wygasa po 15 minutach.</p>',
cta: { label: 'Zaloguj się', url },
footnotesHtml: ['Jeśli to nie Ty prosiłeś/aś o ten link, zignoruj tę wiadomość.'],
});Fields suffixed Html are trusted and emitted as-is — escape untrusted values
with escapeHtml first. cta.label, cta.url and brand are escaped for you.
Templates are pure functions: data in, { subject, html } out. Send with any
SMTP transport. Calling them directly (without a notifier) still works — pass
brand / language / theme per call.
import { bookingConfirmationTemplate, providerNotificationTemplate, cancellationTemplate } from '@nomideusz/svelte-notify';
const { subject, html } = bookingConfirmationTemplate({
guestName: 'Anna',
guestEmail: '[email protected]',
serviceName: 'Morning Vinyasa — Studio X',
slotStartTime: new Date('2026-08-01T18:00:00'),
participants: 2,
totalAmount: 9000, // integer cents
currency: 'PLN',
bookingReference: 'YG-AB12CD34',
language: 'pl', // 'en' | 'pl' | 'uk' — unknown → English
brand: 'szkolyjogi.pl', // footer identity; omit for none
verifyUrl: 'https://example.com/verify/YG-AB12CD34',
qrImageUrl: 'https://example.com/api/qr/YG-AB12CD34', // hosted PNG beats data: URIs in Gmail
});Language
Resolved per call from data.language — no global locale, safe under concurrent server rendering. Interpolation reuses @nomideusz/svelte-i18n's pure interpolate (deliberately not its reactive store).
Escaping
Templates are trusted, params are not. Built-in copy and your overrides may
carry markup (<strong>{service}</strong>); the values substituted into them —
guest names, phones, service names — are HTML-escaped, because they typically
come from a public booking form and providerNotificationTemplate delivers them
to someone else's inbox.
Subjects are a plain-text context and are deliberately not escaped — an
escaped subject shows a literal & in the inbox list.
Composing your own transactional emails alongside these? Use the same helper
rather than declaring a local esc():
import { escapeHtml } from '@nomideusz/svelte-notify';
const html = `<p>Hi ${escapeHtml(user.name)}</p>`;Your voice
Any message key can be replaced per call:
bookingConfirmationTemplate({
...data,
overrides: { b_see_you: 'Do zobaczenia na macie!' },
});All keys and built-in copy live in NOTIFY_MESSAGES (exported).
Templates
bookingConfirmationTemplate— guest confirmation; inlines a QR (hosted PNG preferred, SVG data-URI fallback) whenverifyUrl/qrImageUrlis setproviderNotificationTemplate— heads-up to the instructor/guide/stylist with guest contact detailscancellationTemplate— guest- vs organizer-cancelled copy, refund/no-refund variantsreminderTemplate— pre-arrival nudge with the ticket linkscheduleChangedTemplate— booking moved: old time, new time, free cancellationactionLinkTemplate— link-and-a-button (sign-in, reset, verify, invite)
Plain text
Every template returns { subject, html, text }. Send text as the
text/plain alternative — not instead of html. HTML-only mail costs
deliverability, and the text part is built from the layout's own parts rather
than by stripping tags off the finished document, so links survive as URLs.
const { subject, html, text } = notify.bookingConfirmation(data);
await transporter.sendMail({ to, subject, html, text });Time zones
Times render in the server's zone unless you pin one. Set timeZone on the
notifier ('Europe/Warsaw') so the same booking doesn't read differently
depending on where the process happens to run.
License
MIT
