Render a template
Turn a published email template into personalized HTML, save a preview, and use your existing delivery stack.
BEFORE YOU START
Use a server-side ESM TypeScript project on Node.js 22.23.4 or later, with the SDK installed and your generated client synced. If you’re starting fresh, complete the template and client setup first. The examples use project acme, template welcome-email, and data { name: 'Alex' }.
Prepare your template and data#
Publish an email template with a required string variable named name. Include it in your content, then run npx @usepapier/cli sync so the generated types match the published schema.
Set PAPIER_API_KEY in your server’s environment. Rendering fetches template content from Papier, so you need credentials and a network connection even when you only want a local preview.
renderHTML() needs template data. You can omit a recipient, subject, sender, and delivery adapter.
Render personalized HTML#
Save this script in your application directory. The SDK validates name, resolves the template version range selected in your Papier configuration, and returns a complete HTML document.
import { writeFile } from 'node:fs/promises';
import { createPapier } from '@usepapier/client';
const apiKey = process.env.PAPIER_API_KEY;
if (!apiKey) throw new Error('Set PAPIER_API_KEY on the server.');
const papier = createPapier({
project: 'acme',
apiKey,
environment: 'development',
defaultLocale: 'en',
});
const html = await papier.emails.welcomeEmail.renderHTML({
data: { name: 'Alex' },
});
await writeFile('./welcome-preview.html', html, 'utf8');
console.log('Saved welcome-preview.html');npx tsx --env-file=.env render-template.tsThe return value is a string. Saving it is optional; you can also pass it directly to your application’s preview or delivery code.
Open your local preview#
Open welcome-preview.html from your application directory in a browser. Confirm that the greeting contains “Alex” and the expected styling. This script renders and saves HTML; it doesn’t send an email.
Try changing the data to another name and run the script again. If a required variable is missing or has the wrong type, correct the data to match your schema. When the schema changes in Papier, publish the change and sync the client again.
A browser preview helps you inspect content and layout. Test the final message in your target email clients as well, since their CSS support can differ.
Choose a published language#
To render a French version, first add and publish that translation in Papier. Set defaultLocale when creating the client. The current renderHTML() call accepts data; locale is configured on the client.
import { writeFile } from 'node:fs/promises';
import { createPapier } from '@usepapier/client';
const apiKey = process.env.PAPIER_API_KEY;
if (!apiKey) throw new Error('Set PAPIER_API_KEY on the server.');
const papierFr = createPapier({
project: 'acme',
apiKey,
environment: 'development',
defaultLocale: 'fr',
});
const html = await papierFr.emails.welcomeEmail.renderHTML({
data: { name: 'Alex' },
});
await writeFile('./welcome-preview-fr.html', html, 'utf8');npx tsx --env-file=.env render-locale.tsOpen welcome-preview-fr.html to inspect the result. A locale selects content already available in Papier; it doesn’t translate your template or data at render time.
Send the rendered HTML#
If your application already owns delivery, pass the rendered HTML to that code. This concrete example uses the Resend adapter from the providers guide directly, without registering it on the Papier client.
Save providers.ts beside this script, set RESEND_API_KEY, and replace the sender with an address on your verified domain.
import { createPapier } from '@usepapier/client';
import { createResendAdapter } from './providers.js';
const apiKey = process.env.PAPIER_API_KEY;
const resendApiKey = process.env.RESEND_API_KEY;
if (!apiKey || !resendApiKey) {
throw new Error('Set PAPIER_API_KEY and RESEND_API_KEY on the server.');
}
const papier = createPapier({ project: 'acme', apiKey });
const html = await papier.emails.welcomeEmail.renderHTML({
data: { name: 'Alex' },
});
// Call your provider directly with the HTML. No SDK adapter registration needed.
const provider = createResendAdapter(resendApiKey);
const result = await provider.send({
from: { name: 'Acme', email: 'hello@your-domain.com' },
to: 'alex@example.com',
subject: 'Welcome aboard, Alex',
html,
data: { name: 'Alex' },
});
console.log('Accepted by:', result.provider, result.id);npx tsx --env-file=.env render-and-send.tsDirect provider calls bypass the SDK’s send routing and notification logging. Use the SDK send workflow when you want Papier to run those steps for you.