> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brudcast.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Placeholders and merge tags

> Personalize messages with merge tags, and use fill-once and contact-field placeholders in templates. Syntax, built-in tags and rules.

export const Loop = ({id, src, alt, caption}) => {
  if (!src) {
    return <Placeholder id={id} kind="loop" label="Animation coming soon" description={alt} icon={<PlayIcon />} />;
  }
  return <figure className="brd-media" data-media-id={id}>
      <video className="brd-media-frame" src={src} autoPlay muted loop playsInline aria-label={alt} />
      {caption && <figcaption className="brd-media-caption">{caption}</figcaption>}
    </figure>;
};

export const Screenshot = ({id, src, srcDark, alt, caption}) => {
  if (!src) {
    return <Placeholder id={id} kind="screenshot" label="Screenshot coming soon" description={alt} icon={<ImageIcon />} />;
  }
  return <figure className="brd-media" data-media-id={id}>
      <img className="brd-media-frame block dark:hidden" src={src} alt={alt} />
      <img className="brd-media-frame hidden dark:block" src={srcDark || src} alt={alt} />
      {caption && <figcaption className="brd-media-caption">{caption}</figcaption>}
    </figure>;
};

A merge tag is a name in double braces, such as `{{first_name}}`, that Brudcast replaces with each
recipient's value when a campaign sends. Templates add placeholders on top: named values you
declare once and reuse on every channel.

## Merge tag syntax

* Write a tag as a name inside double braces: `{{first_name}}`. Spaces inside the braces are fine,
  so `{{ first_name }}` works too.
* Names use letters, numbers and underscores.
* A tag is a straight swap for a value. There are no conditions, loops, filters or default values.
* **A tag that doesn't match anything sends as empty text.** An email with a gap where a name should
  be reads better than one that shows `{{first_name}}` to the reader, so Brudcast never sends the
  tag itself.

Tags work in the email subject, preview text and body, the SMS message, and the push title, body
and click URL.

## Built-in tags

| Tag                   | Filled with                                  |
| --------------------- | -------------------------------------------- |
| `{{first_name}}`      | The contact's first name                     |
| `{{last_name}}`       | The contact's last name                      |
| `{{full_name}}`       | The contact's full name                      |
| `{{email}}`           | The address this email is going to           |
| `{{unsubscribe_url}}` | This recipient's unsubscribe link, for email |

Each of your [custom fields](/contacts/custom-fields) is a tag too, named by its key. The
**Insert variable** menu lists the built-in tags under **Contact field** and yours under
**Custom field**. A custom field can't take over a built-in tag's name: `{{email}}` is always the
recipient's address.

If you put `{{unsubscribe_url}}` in your own email, Brudcast leaves its own unsubscribe link out
of the footer, so the email doesn't carry two. See
[The compliance footer](/templates/email-builder#the-compliance-footer).

## Highlighting and warnings

Tags are highlighted as you type, in every field and on the builder canvas, whether you typed them
or picked them from the menu.

In a campaign, if a tag doesn't match a contact field or custom field, a warning at the top of
**Content** names it and says it will send as empty text. It doesn't block the send.

<Screenshot id="ss-templates-placeholders-and-merge-tags-01-warning" alt="The warning above a campaign's content saying a tag does not match a contact field or custom field, so it sends as empty text" />

## Placeholders in templates

In a template, every tag you use has to be declared as a placeholder. There are two kinds:

| Kind              | What it does                                                                                                                                                     | Example                              |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| **Fill once**     | "You type the value once and it applies to every recipient." You're asked for it when you apply the template to a campaign, and it's replaced in the copy there. | `{{product_name}}`, `{{offer_ends}}` |
| **Contact field** | "Resolved per recipient from their contact record when sent." It stays in the campaign as a tag.                                                                 | `{{first_name}}`, a custom field     |

A fill-once placeholder can have a **Label**, a **Default value**, and a **Required** switch. A
required one must have a value, typed in or from its default, before the template can be applied.

A contact-field placeholder's key must match a built-in tag or one of your custom fields. The
built-in tags can't be fill-once: if you type one as the key, the kind switches to **Contact field**.

### Declare a placeholder where you're writing

<Steps>
  <Step title="Open the menu">
    In any field of a template, open **Insert variable** and select **New placeholder**.
  </Step>

  <Step title="Describe it">
    In **New placeholder**, enter the **Key**, choose the **Kind**, and add a **Label** and, for a
    fill-once placeholder, a **Default value**.
  </Step>

  <Step title="Add it">
    Select **Add placeholder**. It's declared for every channel in the template and inserted where
    your cursor was.

    <Loop id="lp-templates-placeholders-and-merge-tags-declare" alt="Choosing New placeholder from the Insert variable menu, naming it offer_code and seeing it inserted in the subject" />
  </Step>
</Steps>

You can also manage placeholders in the **Placeholders** panel: **Add** a new one, or edit or
remove one from its row. If you rename a placeholder that's already in the copy, the dialog warns
that those tags will be undeclared until you update them.

If you type a tag you haven't declared, **Before you save** shows it as used in the copy but not
declared, with a **Declare** button. Brudcast won't save a template that uses undeclared tags.

## Key rules

| Message                                                                 | What it means                                                        |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------- |
| Give the placeholder a key.                                             | The key is empty.                                                    |
| Use lowercase letters, numbers and underscores, starting with a letter. | The key has other characters, or starts with a number or underscore. |
| This key is already used.                                               | Another placeholder in this template has the same key.               |
| … is a reserved merge tag. Switch it to a contact field.                | A built-in tag can't be fill-once.                                   |
| No contact field or custom field has this key.                          | A contact-field placeholder must match an existing field.            |

Keys can be up to 64 characters.

## Merge tags and AI

When AI rewrites or translates copy, it has to return exactly the tags the original had. A
rewrite that drops, adds or renames a tag is thrown away and your original stays, because a broken
tag doesn't fail loudly: it sends as empty text. In a template, AI only uses the placeholders you've
declared. See [Draft with AI](/campaigns/create-a-campaign#draft-with-ai).

## Related

<Columns cols={2}>
  <Card title="Create a campaign" icon="square-pen" href="/campaigns/create-a-campaign">
    Put your personalized copy in front of an audience and send it.
  </Card>

  <Card title="Templates" icon="copy" href="/templates/overview#reuse-a-template">
    Fill placeholders in when you apply a template.
  </Card>

  <Card title="Custom fields" icon="text-cursor-input" href="/contacts/custom-fields">
    Create the fields your tags read from.
  </Card>

  <Card title="Run a migration" icon="languages" href="/migrations/run-a-migration#merge-tags-from-your-old-provider">
    How tags from other providers are converted.
  </Card>
</Columns>
