🔥 500+ people already subscribed. Why not you? Get the monthly newsletter with handy code snippets, tips, and marketing automation insights.

background shape
background shape

When AMPscript Isn’t Enough: Heavy Personalization with JavaScript in Salesforce Marketing Cloud

AMPscript is incredibly powerful for personalization in Salesforce Marketing Cloud (SFMC). It handles token replacement, date functions, conditional logic, and lookups with ease. But there are limits.

When you need to:

  • Group records by categories or subcategories
  • Sort items dynamically before displaying
  • Build complex HTML tables with conditional formatting

…AMPscript alone quickly becomes hard to manage.

This is where Server-Side JavaScript (SSJS) inside Content Builder can be a lifesaver.

The Problem

Imagine you’re pulling records from a Data Extension. You don’t just want to loop over them and print them out – you want to:

  • Group them by Category and Subcategory
  • Sort them alphabetically
  • Do some “ETL”

AMPscript can’t easily handle nested loops, object manipulation, or sorting arrays.

The Approach

The trick is to let JavaScript do the heavy lifting:

  1. Use SSJS to fetch, group, and sort your data.
  2. Build your HTML as a string.
  3. Store that string in an AMPscript variable.
  4. Print it out anywhere in your email.

This hybrid approach combines AMPscript’s personalization features with JavaScript’s flexibility.

Personalized Product Summary with cross-sell

You’re a sportswear retailer — let’s call the brand ActiveGear. Every month, you want to send customers a personalized account summary email that does two things:

  1. Reminds customers what they already own
    • Products are grouped by category (Shoes, Apparel, Accessories).
    • Active items are shown normally.
    • Expired items (for example, products out of warranty) are displayed in gray with a strikethrough.
  2. Suggests complementary products they don’t own yet
    • If a customer owns running shoes, you recommend shoe spray, performance socks, or a sports bottle.
    • If they own a gym bag, you suggest a towel or fitness gloves.
    • If they own apparel, you recommend matching accessories like a cap.

This approach makes the email more than just a static summary — it becomes a personalized shopping assistant that both informs and encourages new purchases.

Data Extension: <strong>UserProducts</strong>

UserEmailProductNameProductCategoryStatusSKUSKU1SKU2SKU3
alice@test.comNike Air ZoomShoesActiveSH001ACC001ACC002APP001
alice@test.comPuma Gym BagAccessoriesExpiredACC010ACC003ACC004
bob@test.comAdidas HoodieApparelActiveAPP020ACC005

Data Extension: <strong>ProductCatalog</strong>

SKUProductNamePrice
ACC001Shoe Spray12 €
ACC002Running Socks18 €
APP001Sports Bottle15 €
ACC003Gym Towel10 €
ACC004Fitness Gloves22 €
ACC005Cap20 €
%%[
var @htmlProducts
set @htmlProducts = ""
]%%

<script runat="server">
Platform.Load("Core","1");

// Current subscriber
var email = Attribute.GetValue("EmailAddress");

// Lookup owned products
var owned = Platform.Function.LookupRows("UserProducts", "UserEmail", email);
var html = "";

if (!owned || owned.length === 0) {
  html += "<p>You have no registered products.</p>";
} else {
  html += "<h2>Your Products with ActiveGear</h2>";

  for (var i=0; i<owned.length; i++) {
    var r   = owned[i];
    var name = r["ProductName"] || "";
    var cat  = r["ProductCategory"] || "";
    var status = r["Status"] || "";
    var style = (status === "Expired") 
      ? "color:#888;text-decoration:line-through;" 
      : "color:#333;";

    html += "<h3 style='margin-top:20px;'>"+ cat +"</h3>";
    html += "<p style='"+style+"'>"+ name +" ("+ status +")</p>";

    // === Related SKUs ===
    var relatedSKUs = [r["RelatedSKU1"], r["RelatedSKU2"], r["RelatedSKU3"]];
    var hasSuggestions = false;

    for (var j=0; j<relatedSKUs.length; j++) {
      var sku = relatedSKUs[j];
      if (!sku) continue;
      var prod = Platform.Function.LookupRows("ProductCatalog", "SKU", sku);
      if (prod && prod.length > 0) {
        if (!hasSuggestions) {
          html += "<p style='font-weight:bold;margin:10px 0 5px;'>You may also like:</p><ul style='margin:0 0 20px 20px;padding:0;'>";
          hasSuggestions = true;
        }
        var p = prod[0];
        var pName = p["ProductName"];
        var pPrice = p["Price"];
        var pUrl = p["Url"];
        html += "<li><a href='"+pUrl+"' style='color:#0096d5;text-decoration:none;'>"+ pName +"</a> - "+ pPrice +"</li>";
      }
    }
    if (hasSuggestions) html += "</ul>";
  }
}

Variable.SetValue("htmlProducts", html);
</script>

%%=v(@htmlProducts)=%%

Other examples

Subscription & Renewal Management (Telecom / SaaS)

  • Scenario: A telecom company sends an account summary email listing all services (Internet, Mobile, TV) for each subscriber.
  • Challenge: Customers may have multiple products in the same category (2 mobile numbers, 3 set-top boxes, etc.). They want expired contracts highlighted, and soon-to-expire ones flagged in orange.
  • Why JS? Sorting subscriptions by product type and status requires grouping, which is clunky in AMPscript.
  • Outcome: The customer gets a clean table of all their subscriptions, encouraging renewals or upgrades.

Travel Itinerary with Activities (Travel / Hospitality)

  • Scenario: A travel agency sends an itinerary email with grouped activities: Flights, Hotels, Tours, Car Rentals.
  • Challenge: Activities must be grouped by day and time, sorted chronologically. AMPscript can’t easily reorder things once fetched.
  • Why JS? JavaScript can sort by datetime, group by “Day X,” and output a clean schedule.
  • Outcome: The traveler gets an itinerary email that looks like a mini dashboard, boosting customer satisfaction.

Insurance Policy Overview (Insurance)

  • Scenario: An insurer sends an annual account summary with all active policies (Car, Health, Property, Life).
  • Challenge: Policies should be grouped by category, sorted by renewal date, with lapsed policies shown in red.
  • Why JS? Handling multiple nested categories and date sorting in AMPscript would be painful.
  • Outcome: Customers clearly see which policies are active, which need renewal, and can click straight to “Renew Now.”

Sorting the arrays or objects in SSJS

In a normal JavaScript environment, you’d rely on

🔒 This content is for Premium Subsribers only.

Please log in to preview content. Log in or Register

You must log in and have a Premium Subscriber account to preview the content.

When upgrading, please use the same email address as your WordPress account so we can correctly link your Premium membership.

Please allow us a little time to process and upgrade your account after the purchase. If you need faster access or encounter any issues, feel free to contact us at info@martechnotes.com or through any other available channel.

To join the Discord community, please also provide your Discord username after subscribing, or reach out to us directly for access.

You can subscribe even before creating a WordPress account — your subscription will be linked to the email address used during checkout.

Premium Subscriber

19,99 € / Year

  • Free e-book with all revisions - 101 Adobe Campaign Classic (SFMC 101 in progress)
  • All Premium Subscriber Benefits - Exclusive blog content, weekly insights, and Discord community access
  • Lock in Your Price for a Full Year - Avoid future price increases
  • Limited Seats at This Price - Lock in early before it goes up

Oh hi there 👋
I have a FREE e-book for you.

Sign up now to get an in-depth analysis of Adobe and Salesforce Marketing Clouds!

We don’t spam! Read our privacy policy for more info.

Share With Others

Leave a Comment

Your email address will not be published. Required fields are marked *

MarTech consultant

Marcel Szimonisz

Marcel Szimonisz

I specialize in solving problems, automating processes, and driving innovation through major marketing automation platforms—particularly Salesforce Marketing Cloud and Adobe Campaign.

Get exclusive technical tips—only available on my blog.

We don’t spam! Read our privacy policy for more info.

Buy me a coffee
Related posts