Nitro · Docs

Shared Asset Configurator

The problem

Shared assets are Blueshift’s reuse mechanism, and they’re ideal for content that should look identical everywhere, like a logo or a footer. The friction shows up when a block needs to vary: a shared asset is static, one copy of the content rendered the same way everywhere it is dropped.

In practice, teams cope in one of two ways. They clone the asset per variation, which defeats the point of a shared asset: ten near-copies that drift apart with every edit, and a library nobody trusts. Or they parameterize by hand, prepending {% assign %} lines above the asset reference, which works but demands that whoever edits the template knows Liquid: the exact variable names, which values need quotes and which don’t, and what happens when one is missing.

Either way, the people who most need to edit these blocks (the marketers shipping the campaign) are the least equipped to do it safely, so every small variation becomes a request to whoever knows the template internals.

What you get

The configurator turns a shared asset into a configurable module: one asset, many safe variations.

  • A form instead of code. Editors configure the block with text inputs, toggles, sliders, color pickers, and dropdowns, with a live preview of the result. No Liquid knowledge required.
  • One source of truth. The asset stays a single shared asset. Fix a bug or restyle it once, and every configured instance picks it up.
  • Guardrails by design. The asset’s author decides exactly what is configurable and within what bounds (options lists, numeric ranges), so brand control survives self-service.
  • No lock-in. The output is ordinary Liquid {% assign %} lines anyone could have typed by hand. Remove Nitro and every template still renders exactly the same.

Overview

A shared asset opts into the configurator by embedding a small config schema inside its content. Nitro reads that schema, renders a form of controls next to the asset, previews the result live, and writes the configured block back into your template.

The schema is plain JSON wrapped in a Liquid comment. Because it lives in a {% comment %}, Blueshift and the email renderer ignore it entirely. It never reaches the inbox. Authoring it is completely opt-in: assets without a schema behave exactly as they do today.

The config block

Add this block anywhere inside the shared asset’s content. The marker nitro:config must directly follow {% comment %}, and the body is a JSON array of field definitions.

{% comment %}nitro:config
[
  { "var": "headline", "type": "text", "label": "Headline", "default": "Big news" }
]
{% endcomment %}
Whitespace-control tags work too: {%- comment -%} and {%- endcomment -%} are both recognized. The first {% endcomment %} after the marker closes the block.

Field definitions

Each item in the array is an object describing one control:

PropertyRequiredApplies toDescription
varYesAll The Liquid variable the control sets. Must be a valid identifier (letters, digits and underscores, not starting with a digit, matching [a-zA-Z_][a-zA-Z0-9_]*) and unique within the schema.
typeYesAll One of the seven field types below.
labelNoAll The label shown in the form. Defaults to the var name if omitted.
defaultNoAll The starting value. If omitted, a sensible per-type default is used (see the table below).
min, max, stepNonumber, range Numeric bounds and increment for the slider / number input.
optionsYes*select, radio A non-empty array of choices. *Required for these two types.

Field types

TypeControlExtra propsDefault when omitted
booleanOn/off toggleNonefalse
textText inputNoneempty string
numberNumber inputmin, max, stepmin, or 0
colorColor pickerNone#000000
rangeSlidermin, max, stepmin, or 0
selectDropdownoptions (required)first option
radioRadio buttonsoptions (required)first option
Quoting is automatic. Nitro knows each field’s type, so string types (text, color, select, radio) are written as quoted Liquid strings, while boolean, number and range are written unquoted. You never quote values in the schema’s default yourself beyond normal JSON.

Full example

A promo banner asset with six controls. The config comment sits at the top; the markup below it uses the same variables. This is the complete content of a shared asset:

{% comment %}nitro:config
[
  { "var": "headline",     "type": "text",    "label": "Headline",      "default": "Big news" },
  { "var": "show_cta",     "type": "boolean", "label": "Show button",   "default": true },
  { "var": "cta_label",    "type": "text",    "label": "Button text",   "default": "Shop now" },
  { "var": "button_color", "type": "color",   "label": "Button color", "default": "#cc0000" },
  { "var": "padding",      "type": "range",   "label": "Padding",  "min": 0, "max": 40, "step": 4, "default": 16 },
  { "var": "align",        "type": "select",  "label": "Alignment", "options": ["left", "center", "right"], "default": "center" }
]
{% endcomment %}
<table role="presentation" width="100%">
  <tr>
    <td style="text-align: {{ align }}; padding: {{ padding }}px;">
      <h1>{{ headline }}</h1>
      {% if show_cta %}
        <a href="https://example.com"
           style="background: {{ button_color }}; color: #fff;
                  padding: 10px 20px; border-radius: 6px;
                  text-decoration: none;">{{ cta_label }}</a>
      {% endif %}
    </td>
  </tr>
</table>

What Nitro writes into your template

When you configure the asset and hit Save changes, Nitro emits one {% assign %} per field (carrying the values you picked), followed by the [shared_asset] reference. Every field is written explicitly, even those left at their default. For the example above with defaults, the block looks like this:

{% assign headline = "Big news" %}
{% assign show_cta = true %}
{% assign cta_label = "Shop now" %}
{% assign button_color = "#cc0000" %}
{% assign padding = 16 %}
{% assign align = "center" %}
[shared_asset] bsft_Promo_Banner [/shared_asset]

Re-opening the block reads those assigns back, so the form pre-fills with whatever the block currently holds, so you can reconfigure it any time.

Try it yourself

Want to see it work end-to-end in your own account? Follow these steps:

  • In Blueshift, create a shared asset (or open an existing one).
  • Paste the full example above as its content and save the asset.
  • In a template, drop the asset into an HTML block on the canvas.
  • With Nitro’s Shared Asset Configurator enabled, click the cog that appears on the block.
  • Adjust the controls, watch the live preview, then click Save changes, followed by Blueshift’s own Save to persist.

Here’s a minimal starter you can paste into a fresh asset to confirm your setup:

{% comment %}nitro:config
[
  { "var": "greeting", "type": "text",   "label": "Greeting", "default": "Hello!" },
  { "var": "accent",   "type": "color",  "label": "Accent",   "default": "#8718af" },
  { "var": "size",     "type": "range",  "label": "Size (px)", "min": 14, "max": 48, "step": 2, "default": 28 }
]
{% endcomment %}
<p style="color: {{ accent }}; font-size: {{ size }}px; font-weight: bold;">
  {{ greeting }}
</p>

Troubleshooting

  • The form doesn’t appear. Confirm the Shared Asset Configurator toggle is on in the Nitro popup, and that the asset content contains the nitro:config marker immediately after {% comment %}.
  • “Config block is not valid JSON.” The body must be a JSON array. Watch for trailing commas, smart quotes, or comments. Standard JSON only.
  • A field is skipped. Each field needs a valid var (unique, valid identifier) and a supported type. select and radio additionally need a non-empty options array.
  • Values look wrong in the preview. Check that your markup references the variables by their exact var names, e.g. {{ headline }}.
Something unclear or missing? Send a note. Docs feedback is just as welcome as bug reports.