Dynamic data controls what goes into the rendered email (merge tags) and what is shown or hidden (context / visibility rules).
Structure
type DynamicData = {
merge_tags?: {
text?: Record<string, string> // paragraph, heading, button text
url?: Record<string, string> // href, src, image URLs
attr?: Record<string, string> // alt, aria-label, etc.
}
context?: Record<string, string | number | boolean>
}
All fields are optional — include only what your template uses.
Merge tags
text — inline content
await client.renderHtml("template-id", {
merge_tags: {
text: {
name: "Noruwa",
company: "Maildeno",
reset_name: "Password",
},
},
})
Values are HTML-escaped before insertion.
Context
Context drives visibility rules — it controls which rows are shown or hidden. Values are never injected into content.
await client.renderHtml("template-id", {
context: {
plan: "pro", // string
country: "usa", // string
age: 25, // number
is_verified: true, // boolean
country_rank: "2", // number-as-string is also fine
},
})
All options together
await client.render({
templateId: "550e8400-e29b-41d4-a716-446655440000",
target: "mjml",
dynamicData: {
merge_tags: {
text: {
name: "Noruwa",
company: "Maildeno",
reset_name: "Password",
},
url: {
reset_url: "https://app.example.com/reset/abc123",
},
attr: {
alt_text: "Cave image",
},
},
context: {
country: "usa",
country_rank: "2",
expiry: "2028",
},
},
})
Missing and extra values
-
A merge tag in the template with no supplied value renders as an empty string.
-
A supplied value for a tag not present in the template is silently ignored.
-
Context keys not matched by any visibility rule are silently ignored.
See Merge Tags and Visibility Rules for the builder-side documentation.