How does a livecard put a page inside a post on X?
A livecard is clickd’s name for a page that plays inside a post on X. It runs on X’s player card: a set of twitter: tags on the page you post that makes X load another page, the one named in twitter:player, in a frame inside the post, in the shape you declare, with an image in its place until it loads. It works only when X's crawler can read those tags and the player route lets X frame it. X's own policy reserves the card for audio and video, so a page that is not a player may show for a while and stop.
GuideBy clickdPublished Updated 10 minute read
- 68,600Pixels the image needs at least, or the player card does not showFrom X's player card reference
- 2 MBThe most HTML X's crawler reads from a pageFrom X's troubleshooting guide
- About 7 daysBefore X reads a page's card tags againFrom X's troubleshooting guide
What is a livecard?
A livecard is a page of yours that opens inside a post, where people already are, with no click away. clickd hosts ready-made ones at /livecard, and this guide shows how to make any page of your own one.
Under it is X's player card, the one card on X that loads a page of yours in a frame inside a post. X's page about cards lists four kinds: a summary card, a summary card with a large image, an app card, and the player card, "a Card that can display video/audio/media". The other three are a picture and some words.
Two addresses matter. The share address is the link someone posts; X's crawler, Twitterbot, fetches it and reads its tags. The player address is the page X puts in the frame, named by twitter:player. They can be the same page, but a route of its own, such as /in-post, is easier to give the headers and the narrow layout the frame needs.
How does a player card work, step by step?
From the post to the frame there are four steps, and the first two happen once, when the link is posted.
How a player card gets from a link to a frame
- The link is postedSomeone posts the share address
- Twitterbot reads itX's crawler fetches the share address and reads its twitter: tags
- X keeps the cardThe tags and the image, kept for that address
- The frame loadsWhen the post is opened, X loads the player address in the frame
- The share page’s tags
- Or an edge layer’s answer
Where the frame shows depends on the client. X's player card reference says that on the web the frame is drawn when the post is expanded, and shown at once on the post's own page, "scaled to fit the appropriate width, maintaining the original aspect ratio". In X's iPhone and Android apps the card shows its image with a play icon in the post's detail view, and a tap opens the player address in a browser inside the app. Other clients show the image and link to the address.
Which tags does a player card need?
These, on the share address, in the page's <head>. The list is X's player card reference; the limits on the title, description and image are from X's cards markup.
| Tag | Required | What it holds |
|---|---|---|
twitter:card | Yes | Set to player. Exactly one on the page. |
twitter:site | Yes | The @username the card is attributed to. |
twitter:title | Yes | The title, 70 characters at most. |
twitter:description | No | One line, 200 characters at most. |
twitter:player | Yes | The https address of the page to frame. |
twitter:player:width | Yes | The frame's width in pixels. |
twitter:player:height | Yes | The frame's height in pixels. |
twitter:image | Yes | The image shown in place of the frame. JPG, PNG, WEBP or GIF, under 5 MB, not SVG, and 68,600 pixels or more. |
<meta name="twitter:card" content="player">
<meta name="twitter:site" content="@acme">
<meta name="twitter:title" content="Hear the first chapter">
<meta name="twitter:description" content="Ten minutes, read by the author.">
<meta name="twitter:image" content="https://acme.example/in-post.png">
<meta name="twitter:player" content="https://acme.example/in-post">
<meta name="twitter:player:width" content="640">
<meta name="twitter:player:height" content="480">- Every address is absolute and on https. The player reference asks for https for the frame and every asset in the tags.
- One
twitter:card. Some frameworks print a second,summary_large_image, from their Open Graph settings; read the page's HTML, not its config. - Leave out
twitter:player:stream. X's markup says it is the "URL to raw video or audio stream", not a page. - Make the image the frame's size, as the reference asks, since it stands in for the frame. One under 68,600 pixels, such as 262 by 262, "will cause the player card not to render".
- Where a
twitter:title, description or image is missing, X's markup says it falls back toog:title,og:descriptionandog:image.
Test a link's card
clickd reads the tags as its own crawler is served them, measures the image, and for a player card reads the player route's framing headers.
Free. Opens the X card tester with what it finds.
How do you let X, and only X, frame the route?
With one response header on the player route, and nothing else on it that refuses a frame. MDN's page on frame-ancestors explains the rule: it names the sites that may embed a page, it is not supported in a <meta> element, and "if any ancestor doesn't match, the load is canceled", which is why the rule names the subdomains of x.com and twitter.com too.
Content-Security-Policy: frame-ancestors 'self' https://x.com https://*.x.com https://twitter.com https://*.twitter.comSend no X-Frame-Options on that route. MDN's page on X-Frame-Options says DENY blocks every frame and SAMEORIGIN every frame but the site's own, and that ALLOW-FROM is obsolete: modern browsers ignore the whole header when they meet it. Every other route keeps frame-ancestors 'self' or 'none'.
Read the headers on the live response, not in the config. A site often sets framing in two places, the server and the framework's middleware, and the one that wins elsewhere can still send DENY on the new route.
What does a framed page need?
An embed mode: the same page, told it is in a frame, changed in four ways. X's troubleshooting guide gives the reason in one line: twitter:player "needs to be a simplified web page with only the player on it", since a full website in the frame shows only part of the site.
- Scrolling. In a post's frame the document may not scroll at all. Make the content fit the declared size, or lock the document and scroll an inner element.
- Links. Open every link in a new tab. Sign-in does not work in a frame on another site: sign-in providers refuse to be framed, and the session cookie is a third-party cookie there.
- Overlays. Hide the cookie banner, the chat widget and any floating button; in a small frame they cover the page.
- Weight. The frame loads when someone opens the post, so keep the route light.
html.framed, html.framed body { height: 100%; overflow: hidden; }
html.framed main { height: 100dvh; overflow-y: auto; overscroll-behavior: contain; }if (window.self !== window.top) document.documentElement.classList.add('framed');
document.addEventListener('click', (event) => {
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
const link = event.target.closest?.('a[href]');
if (!link || link.getAttribute('href').startsWith('#')) return;
event.preventDefault();
window.open(link.href, '_blank', 'noopener');
}, true);The listener runs in the capture phase, so it catches a framework's own links as well as plain ones, and it prevents the default without stopping the event, so click tracking still runs.
How does X keep a card, and how do you change it?
X keeps the card it built for an address. X's troubleshooting guide says its crawler reads a page's card tags again "roughly every seven days", and that images in a card are cached by their address too. A link posted while a tag was wrong can keep the wrong card for days.
So move the route to a new path, or post it with a new query, and give a changed image a new address. The page inside the frame is loaded each time the post is opened, so a fix to the page itself shows on the next open.
What goes wrong, and how do you spot each?
Each failure has a cause you can see from outside. Most of them show in two commands and in clickd's tester.
| What you see | The usual cause | How to spot it |
|---|---|---|
| A plain large image, not a frame | Two twitter:card tags, or an edge layer that answers Twitterbot with another page's card | Fetch the page as Twitterbot and count the twitter:card tags |
| No card at all | robots.txt keeps Twitterbot out, the tags are missing, or the HTML is over the crawler's 2 MB limit | The tester reads robots.txt for Twitterbot and weighs the page |
| A blank or refused frame | frame-ancestors without x.com and twitter.com, X-Frame-Options on the route, or framing set in a meta tag | Read the player route's response headers |
| Only part of the page, or no scrolling | A full page in the frame, with no embed mode | Frame the route yourself in a test page on the same site |
| Sign-in fails inside the frame | Sign-in providers refuse frames, and the cookie is third-party there | Links that open in a new tab |
| The old card after a fix | X keeps the card for the address, read again about weekly | Post the fixed page at a new address |
| No image, or no card on a player | An image under 68,600 pixels, an SVG, 5 MB or more, or not public | The tester measures the image |
| A frame on the web, a link in the app | What X's apps do: the image with a play icon, opened in a browser | Open the post in the app |
The two commands, run from your own machine, ask as X's crawler names itself. The first should print the player tags and one twitter:card; the second the frame-ancestors rule and no X-Frame-Options. A CDN, an edge worker or bot protection often answers social crawlers from a path of its own, so pass the user agent every time, and only the live answer counts.
curl -s -A "Twitterbot/1.0" https://acme.example/launch | grep -o '<meta name="twitter:[^>]*>'
curl -s -o /dev/null -D /dev/stdout -A "Twitterbot/1.0" https://acme.example/in-post | grep -i 'content-security\|x-frame'A checklist for one route
- Give the frame a route of its own, such as
/in-post, never the home page. - Put the player tags on the share address, with one
twitter:cardand every address absolute and on https. - Make the image the frame's size, 68,600 pixels or more, under 5 MB, and not SVG.
- On the player route only, send the
frame-ancestorsrule for x.com and twitter.com, and noX-Frame-Options. - Give the framed page an embed mode: inner scrolling, links in new tabs, overlays hidden.
- Fetch the share address and the player route as Twitterbot, and test the link with the tester.
- Post it from a spare account, open it on the web and in X's apps, and post a new address if anything was wrong.
How do you check it with clickd?
The free X card tester takes the link you will post. It reads robots.txt first, as for Twitterbot, then the card tags as clickd's crawler is served them, measures the image, and for a player card reads the player route's headers host by host. Where the route lets any site frame it, it runs the page in a frame of the declared shape. Where it lets only X, the tester shows the image instead, since clickd.com is not X: that is the rule working. Its second half makes the tags and the header from a few values.
clickd fetches under its own name and never as X's crawler, so it cannot see what an edge layer answers Twitterbot with. The tester gives the two commands above for the link you typed.
For the site's own repository there is a skill for your agent. It picks the route, writes the tags, opens framing to X on that route alone, gives the page its embed mode, fetches the live route as Twitterbot, and reports what it found, the policy among it.
Makes one page of a site a livecard, a page that runs inside a post on X: the player card tags, framing for X on that route only, what X’s crawler is really served, and an embed mode for scrolling, links and overlays, checked live with clickd.
For Claude Code, from the top of the repository, on macOS or Linux
mkdir -p .claude/skills/clickd-livecards/references && curl -fsSL https://clickd.com/skills/livecards/SKILL.md -o .claude/skills/clickd-livecards/SKILL.md && curl -fsSL https://clickd.com/skills/livecards/references/card-tags.md -o .claude/skills/clickd-livecards/references/card-tags.md && curl -fsSL https://clickd.com/skills/livecards/references/framing-headers.md -o .claude/skills/clickd-livecards/references/framing-headers.md && curl -fsSL https://clickd.com/skills/livecards/references/embed-mode.md -o .claude/skills/clickd-livecards/references/embed-mode.md && curl -fsSL https://clickd.com/skills/livecards/references/troubleshooting.md -o .claude/skills/clickd-livecards/references/troubleshooting.md
For Codex, from the top of the repository, on macOS or Linux
mkdir -p .agents/skills/clickd-livecards/references && curl -fsSL https://clickd.com/skills/livecards/SKILL.md -o .agents/skills/clickd-livecards/SKILL.md && curl -fsSL https://clickd.com/skills/livecards/references/card-tags.md -o .agents/skills/clickd-livecards/references/card-tags.md && curl -fsSL https://clickd.com/skills/livecards/references/framing-headers.md -o .agents/skills/clickd-livecards/references/framing-headers.md && curl -fsSL https://clickd.com/skills/livecards/references/embed-mode.md -o .agents/skills/clickd-livecards/references/embed-mode.md && curl -fsSL https://clickd.com/skills/livecards/references/troubleshooting.md -o .agents/skills/clickd-livecards/references/troubleshooting.md
For Cursor, from the top of the repository, on macOS or Linux
mkdir -p .cursor/skills/clickd-livecards/references && curl -fsSL https://clickd.com/skills/livecards/SKILL.md -o .cursor/skills/clickd-livecards/SKILL.md && curl -fsSL https://clickd.com/skills/livecards/references/card-tags.md -o .cursor/skills/clickd-livecards/references/card-tags.md && curl -fsSL https://clickd.com/skills/livecards/references/framing-headers.md -o .cursor/skills/clickd-livecards/references/framing-headers.md && curl -fsSL https://clickd.com/skills/livecards/references/embed-mode.md -o .cursor/skills/clickd-livecards/references/embed-mode.md && curl -fsSL https://clickd.com/skills/livecards/references/troubleshooting.md -o .cursor/skills/clickd-livecards/references/troubleshooting.md
What do people ask about X player cards?
Can any page run inside a post on X?
Not by X's rules. Its player card policy reserves player cards for linear audio and video, and not for purchasing, gaming, polling, messaging or data entry. A page that is not a player goes against that policy, and X may stop showing its frame.
Why does my post show a plain image instead of the frame?
Usually because X's crawler got another card: a second twitter:card on the page, or an edge layer that answers Twitterbot with another page. Fetch the page as Twitterbot and count the twitter:card tags.
Why is the frame blank?
The player route refuses the frame: a frame-ancestors rule without x.com and twitter.com, or an X-Frame-Options of DENY or SAMEORIGIN. Read the live response headers, not the config.
How do I make X read my card again?
X's troubleshooting guide says its crawler reads card tags again roughly every seven days, and caches images by their address. To show a fix at once, post the page at a new address, and give a changed image a new address too.
Does the frame work in X's apps?
Not inline. X's player card reference says its iPhone and Android apps show the card's image with a play icon in the post's detail view, and open the player address in a browser inside the app when it is tapped.
Does clickd fetch my page as Twitterbot?
No. clickd fetches under its own name, with robots.txt obeyed, so an edge layer may answer X's crawler differently. The tester gives the commands to fetch the page as Twitterbot yourself.
Sources
X's card docs, as archived
- Player CardX Developer Platform, archived, February 4, 2026. The required tags, the image, where the frame shows, and the policy
web.archive.org - Cards markupX Developer Platform, archived, February 1, 2026. Every card tag, its limits and its Open Graph fallback
web.archive.org - About Twitter CardsX Developer Platform, archived, February 4, 2026. The four card types
web.archive.org - Troubleshooting CardsX Developer Platform, archived, January 24, 2026. Twitterbot and robots.txt, the 2 MB limit, a simplified page, and the card cache
web.archive.org
Framing headers
- Content-Security-Policy: frame-ancestors directiveMDN. Which sites may frame a page, every ancestor checked, not in a meta element
developer.mozilla.org - X-Frame-Options headerMDN. DENY, SAMEORIGIN, and why ALLOW-FROM is ignored
developer.mozilla.org
Where the figures come from. Every figure on this page is X’s own or MDN’s, from the pages listed under Sources, as archived on the dates shown. None is counted by clickd, and nothing here says X will show a card. A score or a rule passed says pages are well formed. It does not say an assistant cites the site.