Personalization recipes personalization-recipes
This page provides ready-to-use personalization patterns for the most common use cases in Adobe Journey Optimizer. All examples use the personalization editor syntax and can be copied directly into email, SMS, or push content.
For a complete reference of available functions, see Helper functions, Date/time functions, String functions, and Array functions.
Date & time recipes date-time-recipes
Recipe 1 — Display the current date in a readable format recipe-current-date
Use formatDate with getCurrentZonedDateTime() to render today’s date in any format:
{%= formatDate(getCurrentZonedDateTime(), "MMMM dd, yyyy") %}
Output (example): April 11, 2026
Common format patterns:
"dd/MM/yyyy"11/04/2026"MM/dd/yyyy"04/11/2026"EEEE, MMMM dd"Saturday, April 11"yyyy-MM-dd"2026-04-11y (calendar year) rather than Y (week-based year) to avoid unexpected results at year boundaries. See pattern characters for the full reference.Recipe 2 — Countdown to an expiry or event date recipe-countdown
Use dateDiff to compute the number of days remaining until a profile date attribute, then render it dynamically:
Output (example): Your reward points expire in 7 days. Use them before they're gone!
Recipe 3 — X days before a dynamic end date recipe-days-before
To compute a date that is X days before a profile attribute (e.g. to reference in content or subject lines), use addDays with a negative offset:
{%= formatDate(addDays(stringToDate(profile.subscription.endDate), -7), "MMMM dd, yyyy") %}
Output (example): April 04, 2026 (7 days before April 11)
To also set a fixed time of day (e.g. 9 AM), combine with setHours:
{%= formatDate(setHours(addDays(stringToDate(profile.subscription.endDate), -7), 9), "dd/MM/yyyy HH:mm") %}
Recipe 4 — Display current time as HH:MM only recipe-time-only
Use extractHours and extractMinutes to show only the time portion, with a leading-zero guard for minutes:
Output (example): Your appointment is at 14:05.
Recipe 5 — Detect weekend vs. weekday recipe-weekend
Use dayOfWeek to adapt content based on the day. The function returns 1 (Monday) through 7 (Sunday). Use the single = operator (PQL syntax, not ==):
dayOfWeek() is for adapting content based on the day. For routing profiles differently in a journey based on the day of the week, use the built-in Time condition → Day of the week option in the journey Condition activity. Learn moreArray & loop recipes array-recipes
Recipe 6 — List all items from a profile array recipe-list-items
Use {{#each}} to iterate over a profile array and render each item. This is available in the personalization editor (email, SMS, push) only:
Output (example):
- Running shoes: 89€
- Water bottle: 15€
- Gym bag: 45€
{{#each}} is not supported in the journey condition activity. For array filtering in conditions, use collection management functions.Recipe 7 — Show the top N items from an array by price recipe-first-n
Use topN to sort and retrieve the top N items by a numeric field. Since topN is a PQL function, assign it to a variable first with {% let %}, then loop with {{#each}}:
topN(profile.orders, price, 3) sorts orders by price in descending order and returns the top 3 — it does not simply return the first 3 items in the original array order.Or use head to get only the single top item:
{%= head(profile.purchases.recentItems).name %}
Recipe 8 — Render content conditionally per array item recipe-conditional-loop
Use {%#if%} inside {{#each}} to render output only for matching items. Define a loop alias with as |order| so the PQL evaluator can resolve the attribute reference in the condition:
this.status works in Handlebars expressions but is not resolved by the PQL evaluator inside {%#if%}. Using a named loop alias (e.g. order) makes the attribute available to both the Handlebars and PQL contexts.String & formatting recipes string-recipes
Recipe 9 — Clean a string with replaceAll and reuse it recipe-replaceall-reuse
replaceAll returns a new value — it does not modify the original. Use {% let %} to store the result and reference it multiple times without repeating the function call:
Output (example):
Hi John,
Your exclusive code is: WELCOME-JOHN
Recipe 10 — Double-quote a value in JSON output recipe-json-quotes
To include a literal double quote inside a string (e.g. generating JSON for a custom payload), escape it with a backslash (\"):
Output: { "greeting": "Hello \"John\"" }
Recipe 11 — Format a date component in uppercase recipe-uppercase-date
Combine formatDate with upperCase to render month or day names in uppercase:
{%= upperCase(formatDate(getCurrentZonedDateTime(), "MMMM")) %}
Output (example): APRIL
For a full uppercase date string:
{%= upperCase(formatDate(profile.person.birthDateTime, "EEEE MMMM dd yyyy")) %}
Output (example): WEDNESDAY JANUARY 01 2020
Conditional logic recipes conditional-recipes
Recipe 12 — IF/ELSEIF/ELSE in personalized content recipe-if-elseif
Use {%#if%}, {%else if%}, and {%else%} for multi-branch conditional logic. This pattern works in email content and fragments:
Recipe 13 — Null-safe attribute display recipe-null-safe
Use a conditional fallback to avoid rendering empty values when a profile attribute may be null or missing:
Or inline with a ternary-style pattern using isEmpty:
PQL edge case recipes pql-edge-cases
Recipe 14 — Reference a hyphenated attribute key recipe-hyphenated-key
If your XDM schema field name contains hyphens (e.g. order-total, event-type), wrap it in backticks inside a PQL expression to prevent the hyphen from being interpreted as a subtraction operator:
{%= profile.events.`order-total` > 100 %}
{%= ... %}). They are not accepted in plain Handlebars interpolation ({{...}}). If you need to render a hyphenated field value directly, evaluate it via a PQL expression or store it in a variable using {% let %} first.Recipe 15 — Reference a numeric event ID in a context attribute recipe-numeric-event-id
When using a journey context event whose ID is a numeric string (e.g. 1697323153), wrap the ID in backticks and use {% let %} with toDateTime() and formatDate():
Output (example): Your appointment: 18/03/2026 14:30
Recipe 16 — Type coercion: compare a string field to a number recipe-type-coercion
PQL is strongly typed. When a profile field is stored as a string but you need to compare it numerically, convert it first with stringToNumber():
{%= stringToNumber(profile.loyalty.pointsBalance) > 500 %}
For boolean fields stored as strings:
{%= toBool(profile.consents.email.val) = true %}