Standard X embeds load tracking scripts and set cookies on every page view. This site uses astro-tweet to fetch posts at build time and render them as static HTML. X never knows when someone views the embedded post.
The standard embed problem
X’s official embed loads platform.twitter.com/widgets.js in every visitor’s browser. That script:
- Sets tracking cookies
- Phones home to X’s analytics
- Loads additional resources
- Executes arbitrary JavaScript on your page
YouTube has youtube-nocookie.com as a privacy alternative. X doesn’t offer anything equivalent.
Build-time fetching
This site uses astro-tweet, which takes a different approach. Instead of loading X’s widget at runtime, it fetches post data during the Astro build:
Build time: Your server → X syndication API → Static HTML
Page view: Visitor → Your static HTML (no X connection)The syndication API is the same endpoint X’s widget uses internally. The difference is when and where it runs.
How it works
X’s public syndication endpoint at cdn.syndication.twimg.com/tweet-result returns JSON with the post content, author info, media, and engagement counts. The astro-tweet package calls this once per post during build, then renders the response to HTML.
The authentication is just a hash of the post ID:
function getToken(id: string) {
return ((Number(id) / 1e15) * Math.PI)
.toString(6 ** 2)
.replace(/(0+|\.)/g, "");
}No API keys, no OAuth, no rate limits for reasonable usage.
Example
Here’s a post rendered this way:
View source on that. It’s pure HTML and CSS. No iframes, no external scripts, no tracking pixels.
Trade-offs
What you lose:
- Live engagement counts - likes and retweets freeze at build time
- Deleted post handling - if the author deletes it, yours stays until you rebuild
- Native interactivity - no “like from embed” functionality
What you keep:
- Full visual fidelity (I style it to match this site’s aesthetic)
- All media, links, and author info
- Zero JavaScript for the embed
- Complete privacy for your visitors
Comparison
| Approach | Tracking | JS Required | Live Data |
|---|---|---|---|
| astro-tweet (this site) | None | No | Static |
| Standard X embed | Full | Yes | Yes |
| YouTube nocookie | Some | Yes | Yes |
Build-time fetching is actually more private than YouTube’s nocookie option, which still loads Google’s player code in the browser.
Usage
import Tweet from '../components/Tweet.astro';
<Tweet id="1694368034755096857" />
// or
<Tweet id="https://x.com/tonyseets/status/1694368034755096857" />The component accepts either a bare ID or a full URL.