Programmatic Templates
Most users will design templates visually in the drag-and-drop editor. But sometimes you need to create templates in code -- seeding default content, building templates from API data, or generating emails programmatically on the server.
Templatical provides factory functions for every block type. Use them to build templates in code, then pass them to init() or render them directly with the renderer.
Blank template
import { createDefaultTemplateContent } from '@templatical/types';
const content = createDefaultTemplateContent();
// { blocks: [], settings: { width: 600, backgroundColor: '#ffffff', fontFamily: 'Arial' } }createDefaultTemplateContent() accepts an optional font family string:
const content = createDefaultTemplateContent('Georgia, serif');Building a template
Every block type has a corresponding create*Block() function. Each accepts an optional partial object to override defaults. All factory functions auto-generate a unique id for each block.
import {
createDefaultTemplateContent,
createTitleBlock,
createParagraphBlock,
createImageBlock,
createButtonBlock,
createDividerBlock,
} from '@templatical/types';
const content = createDefaultTemplateContent();
content.blocks = [
createTitleBlock({
content: '<h1 style="text-align: center;">Welcome aboard</h1>',
level: 1,
}),
createImageBlock({
src: 'https://example.com/hero.jpg',
alt: 'Welcome hero image',
width: 'full',
}),
createDividerBlock(),
createParagraphBlock({
content: '<p>Thanks for signing up. Here is what happens next.</p>',
}),
createButtonBlock({
text: 'Get Started',
url: 'https://example.com/dashboard',
backgroundColor: '#1a73e8',
textColor: '#ffffff',
borderRadius: 6,
}),
];Block factory reference
Title
createTitleBlock({
content: '<h1>Welcome, {{name}}!</h1>',
level: 1,
textAlign: 'center',
})Paragraph
createParagraphBlock({
content: '<p>Thanks for signing up. Here is what happens next.</p>',
})Image
createImageBlock({
src: 'https://cdn.example.com/hero.png',
alt: 'Hero banner',
width: 560,
linkUrl: 'https://example.com',
})Button
createButtonBlock({
text: 'Get Started',
url: 'https://example.com/signup',
backgroundColor: '#6366f1',
borderRadius: 8,
})Divider
createDividerBlock({
lineStyle: 'dashed',
color: '#e5e7eb',
thickness: 2,
})Spacer
createSpacerBlock({ height: 40 })HTML
createHtmlBlock({
content: '<div style="text-align:center;">Custom markup</div>',
})Social Icons
createSocialIconsBlock({
iconStyle: 'circle',
iconSize: 'large',
icons: [
{ id: crypto.randomUUID(), platform: 'twitter', url: 'https://x.com/acme' },
{ id: crypto.randomUUID(), platform: 'github', url: 'https://github.com/acme' },
],
})Menu
createMenuBlock({
items: [
{ id: crypto.randomUUID(), text: 'Home', url: 'https://example.com', openInNewTab: false, bold: false, underline: false },
{ id: crypto.randomUUID(), text: 'Blog', url: 'https://example.com/blog', openInNewTab: false, bold: false, underline: false },
{ id: crypto.randomUUID(), text: 'Docs', url: 'https://docs.example.com', openInNewTab: true, bold: false, underline: false },
],
separator: '-',
})Table
createTableBlock({
hasHeaderRow: true,
rows: [
{ id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Plan' }, { id: crypto.randomUUID(), content: 'Price' }] },
{ id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Starter' }, { id: crypto.randomUUID(), content: '$9/mo' }] },
{ id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Pro' }, { id: crypto.randomUUID(), content: '$29/mo' }] },
],
})Video
createVideoBlock({
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg',
alt: 'Product demo video',
})Section
createSectionBlock({
columns: '2',
children: [
[createParagraphBlock({ content: '<p>Left column</p>' })],
[createImageBlock({ src: 'https://cdn.example.com/photo.jpg' })],
],
})The columns property accepts: '1' (single), '2' (two equal), '3' (three equal), '2-1' (two-thirds / one-third), '1-2' (one-third / two-thirds). See Sections and Columns for full details.
Custom
createCustomBlock takes a CustomBlockDefinition (not a partial block). It generates field values from the definition's field defaults. See Custom Blocks for defining custom block types.
Utilities
Generic factory
Create any block by type string:
import { createBlock } from '@templatical/types';
const block = createBlock('title'); // TitleBlock with defaultsCloning
Deep-clone a block with a new ID:
import { cloneBlock } from '@templatical/types';
const copy = cloneBlock(existingBlock);
// copy.id !== existingBlock.idType guards
Narrow a Block union to a specific type:
import { isTitle, isParagraph, isImage, isButton, isSection } from '@templatical/types';
if (isTitle(block)) {
console.log(block.level); // TypeScript knows this is TitleBlock
}
if (isParagraph(block)) {
console.log(block.content); // TypeScript knows this is ParagraphBlock
}
if (isImage(block)) {
console.log(block.src);
}Every block type has a corresponding guard: isTitle(), isParagraph(), isImage(), isButton(), isDivider(), isSpacer(), isHtml(), isSocialIcons(), isMenu(), isTable(), isVideo(), isSection(), isCustomBlock().
Template settings
Template settings control the global properties of the email:
const content = createDefaultTemplateContent();
content.settings.width = 640;
content.settings.backgroundColor = '#f5f5f5';
content.settings.fontFamily = 'Helvetica, Arial, sans-serif';
content.settings.preheaderText = 'Your weekly digest is here';| Setting | Type | Description |
|---|---|---|
width | number | Email width in pixels |
backgroundColor | string | Outer background color |
fontFamily | string | Default font stack |
preheaderText | string | Preview text shown in inbox list |
For default values and how to customize them, see Block & Template Defaults.
Loading saved content
Pass previously saved JSON back to the editor:
const saved = await fetch('/api/templates/123').then(r => r.json());
const editor = await init({
container: '#editor',
content: saved,
});You can also update content after initialization:
editor.setContent(newContent);Next steps
- Block Types -- properties reference for all 14 block types.
- How Rendering Works -- the JSON → MJML pipeline.
- Custom Blocks -- define your own block types.