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 %}
{%- 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:
| Property | Required | Applies to | Description |
|---|---|---|---|
var | Yes | All | 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. |
type | Yes | All | One of the seven field types below. |
label | No | All | The label shown in the form. Defaults to the var name if omitted. |
default | No | All | The starting value. If omitted, a sensible per-type default is used (see the table below). |
min, max, step | No | number, range |
Numeric bounds and increment for the slider / number input. |
options | Yes* | select, radio |
A non-empty array of choices. *Required for these two types. |
Field types
| Type | Control | Extra props | Default when omitted |
|---|---|---|---|
boolean | On/off toggle | None | false |
text | Text input | None | empty string |
number | Number input | min, max, step | min, or 0 |
color | Color picker | None | #000000 |
range | Slider | min, max, step | min, or 0 |
select | Dropdown | options (required) | first option |
radio | Radio buttons | options (required) | first option |
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:configmarker 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 supportedtype.selectandradioadditionally need a non-emptyoptionsarray. - Values look wrong in the preview. Check that your markup references the variables by their exact
varnames, e.g.{{ headline }}.