Appearance
Saved Templates Deep Dive
Create reusable template configurations for consistent branding.
Overview
Saved templates let you:
- Store frequently used configurations
- Maintain brand consistency
- Reduce API payload size
- Share settings across your team
Creating Templates
Via Dashboard
- Go to Dashboard → Templates
- Click "Create Template"
- Configure your settings
- Save with a unique ID
Via API
javascript
const response = await fetch('/api/user-templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
template_id: 'company-blog-dark',
template_data: {
template: 'blog',
theme: 'dark',
logo_url: 'https://mycompany.com/logo.png',
author_avatar_url: 'https://mycompany.com/default-avatar.png'
}
})
});Template Organization
Naming Conventions
Use descriptive, hierarchical IDs:
{purpose}-{variant}-{theme}
Examples:
- blog-standard-dark
- blog-featured-light
- product-sale-dark
- email-welcome-lightCategorization
Create templates for each use case:
| Category | Template IDs |
|---|---|
| Blog | blog-default, blog-tutorial, blog-news |
| Products | product-standard, product-sale, product-new |
| Marketing | social-announcement, social-milestone |
email-welcome, email-receipt, email-newsletter |
Template Inheritance
Merge saved templates with request-time parameters:
javascript
// Saved template
{
template_id: 'blog-company',
template_data: {
template: 'blog',
theme: 'dark',
logo_url: 'https://company.com/logo.png'
}
}
// Request
{
user_template_id: 'blog-company',
title: 'New Post Title',
subtitle: 'Post description'
}
// Result: merged
{
template: 'blog',
theme: 'dark',
logo_url: 'https://company.com/logo.png',
title: 'New Post Title',
subtitle: 'Post description'
}Override Behavior
Request parameters override template settings:
javascript
// Template has theme: 'dark'
// Request has theme: 'light'
// Result: theme is 'light'Implementation Patterns
Factory Pattern
javascript
class OGImageFactory {
constructor(apiKey) {
this.apiKey = apiKey;
this.templates = new Map();
}
async loadTemplates() {
const response = await fetch('/api/user-templates', {
credentials: 'include'
});
const data = await response.json();
for (const template of data.templates) {
this.templates.set(template.template_id, template.template_data);
}
}
async generate(templateId, overrides = {}) {
const baseTemplate = this.templates.get(templateId);
if (!baseTemplate) {
throw new Error(`Template not found: ${templateId}`);
}
const params = { ...baseTemplate, ...overrides };
return fetch('/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey
},
body: JSON.stringify(params)
});
}
}
// Usage
const factory = new OGImageFactory(apiKey);
await factory.loadTemplates();
const image = await factory.generate('blog-company', {
title: 'My Post',
subtitle: 'Description'
});Template Service
javascript
// services/og-templates.js
class OGTemplateService {
static TEMPLATES = {
'blog-standard': {
template: 'blog',
theme: 'dark',
logo_url: process.env.LOGO_URL
},
'blog-guest': {
template: 'blog',
theme: 'light'
},
'product-default': {
template: 'product',
theme: 'dark',
logo_url: process.env.LOGO_URL
},
'product-sale': {
template: 'product',
theme: 'dark',
badge: 'SALE',
logo_url: process.env.LOGO_URL
}
};
static getParams(templateId, overrides) {
const base = this.TEMPLATES[templateId];
if (!base) throw new Error(`Unknown template: ${templateId}`);
return { ...base, ...overrides };
}
}Multi-Brand Support
For agencies or multi-tenant platforms:
javascript
const brandTemplates = {
'brand-a': {
'blog': {
template: 'blog',
theme: 'dark',
logo_url: 'https://brand-a.com/logo.png'
}
},
'brand-b': {
'blog': {
template: 'blog',
theme: 'light',
logo_url: 'https://brand-b.com/logo.png'
}
}
};
function getTemplate(brandId, templateType) {
return brandTemplates[brandId]?.[templateType];
}Versioning Templates
Track template changes over time:
javascript
const templates = {
'blog-v1': { /* old config */ },
'blog-v2': { /* new config */ }
};
// Or use date-based versions
const templates = {
'blog-2024-01': { /* config */ },
'blog-2024-06': { /* updated config */ }
};Best Practices
- Use descriptive IDs — Clear naming helps organization
- Group by purpose — Blog, product, marketing, etc.
- Include brand assets — Logos, default avatars
- Version your templates — Track changes over time
- Document internally — Keep a template catalog
- Test before deploying — Preview each template
Template Limits
| Plan | Max Templates |
|---|---|
| Free | 5 |
| Pro | 50 |
| Business | Unlimited |
Migration Between Environments
Export templates for different environments:
javascript
// Export
const templates = await fetch('/api/user-templates', { credentials: 'include' })
.then(r => r.json())
.then(d => d.templates);
fs.writeFileSync('templates.json', JSON.stringify(templates, null, 2));
// Import
const templates = JSON.parse(fs.readFileSync('templates.json'));
for (const template of templates) {
await fetch('/api/user-templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
template_id: template.template_id,
template_data: template.template_data
})
});
}