Back to News & Insights
Web Development October 13, 2026 · 6 min read

Integrating SEO Components into Discord

If you paste a link to your website in a Discord channel, a small card appears automatically: title,...

Integrating SEO Components into Discord

If you paste a link to your website in a Discord channel, a small card appears automatically: title, description, and an image. You didn’t have to write any special code for this, it’s the default behavior.

Discord has just added another option: to completely replace this card with a custom preview, featuring formatted text, an image gallery, and buttons.

Here's what you need to have set up before you start: A site served over HTTPS Open Graph tags already present (required, even with an embed component) Server-side rendering (the HTML must contain the tags in the response itself, not after a JavaScript script) The Discordbot crawler is allowed if a WAF, CDN, or rate limiter is in front of the site The embed component's JSON payload is ready (we'll get to that next)

Default Behavior: Open Graph Tags Without going into further detail, here's what happens: Discord sends a request to your URL, reads the HTML returned by the server, and looks for Open Graph tags in the element.

Here are three key points about how this works, because they’re important for what comes next as well: The tags must be in the HTML sent by the server. Discord does not execute any JavaScript; a tag added later by client-side JS does not exist for Discord. The crawler identifies itself with the user-agent Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com). If a WAF or rate limiter blocks bots, you must explicitly allow Discordbot through. The preview is cached for about 30 minutes. To force a refresh during testing, simply add ?v=2 to the URL.

I go into detail about these Open Graph tags (image sizes, Twitter Card tags, etc.) in "A Few Tips for Improving Your SEO" if you want to explore that topic further.

If there are no embed components on the page, Discord displays nothing more than the card shown above.

This is an addition, not a mandatory replacement: the og: tags are still required in all cases. They serve as a fallback if the embed cannot be displayed (e.g., a context that doesn’t support it, an invalid payload, etc.).

On the crawler side, the process is the same as for the standard card, with one additional step: A link to your page is posted in a Discord message. Discord fetches the URL and reads the HTML. It also looks for an embed component tag. If it exists, it parses the JSON and retrieves the metadata for each image in the payload. If the payload is valid, it replaces the Open Graph card with this custom rendering. Otherwise, it goes back to the previous step.

Option 2, in external JSON, with a link pointing to a file on the same domain (or a subdomain):

This option has a strict limit: 3,000 bytes for the raw file. For an embed with an image gallery and buttons, the inline option allows for a larger size.

It can contain up to 40 components in total, from among those listed in the table below. Any other component type invalidates the entire payload:

| Type | Component | Notes | | - | - | - | | 1 | Action Row | row of buttons | | 2 | Button | link style only (style: 5), with label and/or emoji | | 9 | Section | text + an element (thumbnail or button) | | 10 | Text Display | Discord Markdown (headings, bold, lists, links...) | | 11 | Thumbnail | small image attached to a Section | | 12 | Media Gallery | up to several images/videos, in large format | | 14 | Separator | separator line | | 17 | Container | the root component |

For images (thumbnails and Media Gallery), you only need to provide the URL; Discord will automatically retrieve the dimensions and file type:

Supported formats: PNG, GIF, JPEG, WebP, AVIF, and for the Media Gallery, MP4/MOV/WebM as well.

On every post on my blog, in addition to the standard Open Graph tags (which are still there, hidden), I now include an embed component.

It’s built using the same data as the standard card: the title, description, cover image, and the post’s accent color.

Some decisions made while building it: Media Gallery instead of Thumbnail: At first, I went with a Section + Thumbnail, but the image looked tiny next to the text. The Media Gallery displays the image in full size, like a real banner. No Separator: It visually broke up the embed for no reason between the text and the image. Two buttons: one to read the article, one to return to the blog. Simple, but it provides an additional entry point directly from Discord. accentcolor: this is an integer, not a hexadecimal color. This blog already has a color associated with each article (backgroundColor in the frontmatter). I simply convert it to decimal (parseInt(hex.replace(“#”, “”), 16)) rather than managing a new one.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation