Skip to main content
Embeds are fine, but they’re rigid. Containers are Discord’s newer layout system: a bordered block with a coloured accent bar that you fill with text, images, thumbnails and buttons in whatever order you like. Anywhere you can write an embed script, you can write a container instead.
Rather build it visually? The message builder previews containers exactly like Discord, shows you the script as you go, and can send the message straight to a channel.

Opening a container

Everything after it that starts with box. (or container., etc.) belongs to that container. Open a second {box} to start a new one. {layout} works the same way — {layout.text: ...}, {layout.media: ...} and so on — for text, galleries or button rows that sit directly in the message, above or below your containers.

What goes inside

Both support full Discord markdown, including # headings, -# subtext, lists and > quotes. A header without its own # renders at ## size.
Aliases: sep, divider, hr. large (or lg / big) adds more breathing room. invisible (or hidden) keeps the spacing but drops the line — {box.spacer} is shorthand for that.
Aliases: image, img, gallery, photo, pic. Up to 10 items separated by |, laid out in Discord’s gallery grid. Put spoiler: in front of a URL to blur it, and anything after the URL becomes its alt text.Only real https:// links are sent — an image that fails to resolve (say, a variable that came out empty) is left out rather than breaking the whole message.
A section is up to three blocks of text with a thumbnail — or a button — beside them.
Aliases: sec. {box.thumbnail: <url>} next to a {box.text} pairs the two into a section as well.Quotes inside a quoted value are written as \".
Buttons written one after another share a row, the way Discord lays them out — up to 5 per row, after which a new row starts. Anything else between them, or {box.row}, ends the row.
Button replies keep working forever — they survive restarts and never time out. A non-link button with no message just acknowledges the click.Top-level {button: ...} nodes work the same way, and {row} starts a new row for them too.
Shows a file that was uploaded with the message. Discord only accepts uploaded files here, so this is mainly for the message builder, which attaches the files for you.
Set on the opening node, or separately afterwards:
{box.dominant} pulls the colour from the first Discord-hosted image in the container — section thumbnails first, then media gallery images.A container has no author or footer, so there are only two slots worth naming:
Same rules as embeds: the separator can be a dot, colon or space, and a named slot is preferred rather than required.

Container vs embed

Neither is better — they’re for different shapes of message. Discord doesn’t let a Components V2 message carry plain content or embeds, so as soon as a script uses a container Mira adapts the rest for you: {content} becomes loose text in the same spot, top-level buttons become a loose button row, and each embed is turned into a container that reads the same way. Stickers can’t be sent alongside containers.

A worked example

A welcome message that actually looks designed:
Build it with !embed first so you can see it, tweak it, and only paste it into !welcome add once you’re happy. !copyembed on the result gives you the script back if you lose it.

Limits

Discord enforces these, not the bot:
  • 40 components total per message (every text, button, thumbnail and container counts)
  • 5 buttons per row
  • 4000 characters across all text in the message
  • 10 items per media gallery, 3 texts per section
Going over any of these means the message won’t send. If a script silently fails to appear, this is usually why — trim it down and try again. The message builder counts all of these for you as you go.