Skip to main content

Overview

OneSignal provides several ways to personalize message content at scale. This guide focuses on using the Dynamic Content with CSV upload feature found in the OneSignal dashboard for push, email, and SMS. Key benefits:
  • Use a CSV to personalize at scale – One message, custom experiences for everyone
  • Multi-language support – Automatic language switching per user
  • Dynamic segmentation – Content adapts to user properties (language, region, campaign ID)
  • Team collaboration – Non-technical users edit content in CSV files
  • Cross-channel compatibility – Reuse CSV logic across channels
Common use cases:
  • Multi-language onboarding or marketing
  • Region-specific promotions
  • Event announcements per location
  • Campaign-based personalization

Dynamic Content with CSV setup steps

Quick reference:
  1. Create a CSV file with your content variations.
  2. Map the CSV data to the message using the dynamic_content property in liquid syntax.
  3. Create a new message or template from the OneSignal dashboard.
  4. Select the Dynamic Content or Personalization button.
  5. Upload the CSV file and send the message.

CSV requirements & setup

  • File size: Under 200 KB
  • Column headers:
    • Reserve the first column header for the tag key or leave blank to reference sections
    • Alphanumeric characters and underscores only
    • Use underscores (_) instead of spaces
  • Encoding: UTF-8
Start with a blank CSV or use a provided template. Templates are provided when selecting the Dynamic Content or Personalization button in the message and template editors.
Templates are available for:
  • Multi-language – Localize content by language
  • Content personalization – Customize content by Data Tags

Example CSVs

This guide will use the following example CSV data.
  • Map the column headers to your supported language codes.
  • Add your translations to each row for each language code.
  • If you have multiple sections (like in an email), designate the first column as the section name.
In this example:
  • We have 3 languages: English, Spanish, and French.
  • We have 2 sections: “section_1” and “section_2”.

Map CSV data to message content

Using Liquid syntax, reference the CSV data in your message using the dynamic_content property:
{{dynamic_content.file_name.message_component[user_property]}}
or
{{dynamic_content.file_name[user_property].message_component}}
Parameters:
  • dynamic_content – The property name used to reference the CSV data
  • file_name – CSV file name (without .csv extension)
  • message_component – The specific message component you want to personalize. This is the static text in the CSV column header or first row.
  • user_property – The user property you want to reference.
Fallback content: Always use hard-coded string default fallbacks to ensure messages render if the CSV lookup or Dynamic Content fails.
Liquid syntax for the fallback
{{ dynamic_content.my_template.header[user.language] | default: "Welcome to our latest update" }}
This means if the CSV lookup or Dynamic Content fails, the message will render the fallback text "Welcome to our latest update". This ensures:
  • Dynamic Content is used when available
  • A hard-coded message appears if Dynamic Content fails
  • Users never receive blank content
translations.csv
,en,es,fr
section_1,Hello,Hola,Bonjour
section_2,Thanks for shopping with us...,Gracias por comprar con nosotros...,Merci pour votre achat avec nous...
  • The file_name is translations.csv.
  • The message_component is in the rows of the first column section_1 and section_2.
  • The user_property is the column header matching language code. We can reference this on the user with the user.language property.
Basic Liquid syntax for the multi-language message
{{dynamic_content.translations.section_1[user.language]}}
{{dynamic_content.translations.section_2[user.language]}}
(Recommended) Liquid syntax with default fallback for the multi-language message
{% assign supported_langs = "en,es,fr,de" | split: "," %}
{% assign lang = user.language | default: "en" %}

{% unless supported_langs contains lang %}
  {% assign lang = "en" %}
{% endunless %}

{{ dynamic_content.translations.section_1[lang] | default: "Hello" }}
{{ dynamic_content.translations.section_2[lang] | default: "Thanks for shopping with us..." }}
Use Liquid with default fallback to update subject lines, preheaders, button labels, and URLs.

Usage considerations

How can I test the Dynamic Content with CSV?

We recommend using email to test multiple variations of the message.
  • You can use the + addressing in emails to test multiple variations: [email protected]
  • Set tags follwing the above multi-language and content personalization examples.
  • See Import for more on uploading multiple users and data tags.

When to use Dynamic Content with CSV vs. other personalization options

  • Use Dynamic Content with CSV if you are sending messages from the dashboard and have access to user data with a CSV file.
  • For other options of addign dynamic content to messages, see Message Personalization or Multi-language Messaging options.

Updating templates

Re-upload CSVs via dashboard or use the Update Template API dynamic_content property.

Special characters in keys

Hash notation (for non-alphanumeric keys):
{{ dynamic_content.file_name["!the_row!"]["&the_column&"] }}
Dot notation (for standard keys):
{{ dynamic_content.file_name.the_row.the_column }}