Component embeds, in plain language
Discord component embeds let any page ship its own link preview with no app and no bot. The docs are still an open pull request, and the payload has a 3,000 byte cap.
Discord shipped a way for any page to render its own link preview, and the documentation for it is still sitting in an open pull request. PR #8606 in discord-api-docs opened on 16 September 2026 and was last touched on 30 September. It is 531 added lines across 17 files, and the two new pages under developers/link-previews/ account for 463 of them, describing a payload capped at 3,000 bytes that has to survive a fetch with a 10 second deadline. That part still surprises me.
A page can now hand Discord a JSON payload and get a components container back instead of the usual Open Graph card. It rides along as an inline <script id="discord:component-embed" type="application/json">, or as a <link rel="discord:component-embed"> pointing at a JSON file somewhere on your own domain. At the top level sits one Container component, type 17. Up to 40 components fit inside it, counting the container itself.
Then the constraints, which are where this gets interesting if you are shipping a static site:
- The tag has to be in the HTML your server sends. Discord runs no JavaScript, so a tag injected after page load is never seen.
- The response must be 2xx, follow no more than 5 redirects, stay under 50 MB, and finish the whole fetch, images included, inside 10 seconds.
- The payload is capped at 3,000 bytes either way, and escape sequences count at their full length.
- A linked
hrefmust be absolute, and its host must be the page’s host, a subdomain of it, or its parent.
The failure mode I’d bet on seeing most is a WAF rule that permits bots by name and still blocks the request that counts. Pages come in as Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com), while the linked JSON file comes in as bare Discordbot/2.0. An allowlist written for the short string misses the HTML request completely. Discord never documented any of this, so treat it as field notes.
{
"component": {
"type": 17,
"accent_color": 1752220,
"components": [
{ "type": 9, "components": [{ "type": 10, "content": "# Patch notes" }],
"accessory": { "type": 2, "style": 5, "url": "https://example.com/notes", "label": "Open" } }
]
}
}
One more trap, and it runs against the usual advice. Going over 40 components doesn’t degrade to your Open Graph fallback. The embed stops rendering, full stop. A fallback covers a payload Discord can’t parse. An oversized one gets nothing, which means a page can look right in dev and ship a blank card.
What fits inside is narrower than the full components set. Action Row (1), Section (9), Text Display (10), Thumbnail (11), Media Gallery (12), Separator (14), Container (17), plus Button (2) with link style only. Text Display takes Discord markdown. A Media Gallery caps at 10 items across the whole embed rather than per component, and thumbnails don’t count toward that.
Buttons are the only interactive part, which makes them inert. Style 5 and nothing else. The allowed keys are type, url, style, label, emoji, and disabled; add one extra key, including id, custom_id, or sku_id, and Discord rejects the whole payload rather than the button. If you were holding out for a SKU button, it isn’t coming through this door.
For anyone reading this with a bot in hand, the API moved as well. The message object gains a components embed type and a components array on the embed holding that one Container, so bot-side handling can branch on embed type where it previously only saw rich, image, video, gifv, article, link, or poll_result.
Previews cache for about 30 minutes, so editing the JSON and resharing the same URL hands you the old card. Share with a fresh query string, ?v=2, to see changes immediately, or open Discord’s Embed Debugger at discord.com/developers/embeds, which reports which tags it picked and which layout it chose.
Why this earns an afternoon for anyone in community ops: it moves the work from a Discord app and a bot to a page and some HTML. The discipline shifts from keeping an app alive to keeping markup correct, and every new failure mode is a front-end failure mode. Server rendering and byte limits are problems I can debug. A WAF that quietly eats the crawler is a different kind of Tuesday.
The first page I’d try is release notes that currently render as a flat card. Drop in a Section with a Text Display and a Thumbnail. Share the URL with ?v=2 and look. If it comes back with an accent color and a thumbnail, it earned its place. And if it comes back blank, check the user agent before you start rewriting the JSON.