Twitter Timeline Embed Not Working? 4 Fixes for 2026
Fix a Twitter timeline embed not working in 2026: four checks we tested on X's widget, and a Node script that turns any account's posts into plain HTML.
Tarun Khatri
Published · Updated · 7 min read
If your Twitter timeline embed is not working, check the timeline type, your page's code, the account's privacy and the visitor's browser.
X (Twitter) still serves embedded timelines in 2026. You get four fixes, then a backup feed that needs no X script. We run Weir, the API the backup uses.
Start from fresh code. Yours should look like what publish.x.com gave us for @NASA on 22 October 2026:
<a class="twitter-timeline" href="https://x.com/NASA?ref_src=twsrc%5Etfw">Posts by NASA</a>
<script async src="https://platform.x.com/widgets.js" charset="utf-8"></script>
- Use a live timeline typeProfile or List only.
- Keep the scriptRaw HTML block, X's domains allowed.
- Re-run the widgettwttr.widgets.load() in single-page apps.
- Check account and browserProtected posts never embed.
- Or skip the widgetServe cached posts as HTML.
Is the Twitter timeline embed not working for everyone?
No. It loaded for us with no X account, so check your own setup first.
X's developer docs and Help Center both still list Profile and List timelines. On 22 October 2026 we requested the frame X's widget loads for @NASA, with no login, and got NASA's 20 newest posts.
We found no 2026 notice from X retiring these timelines. So match what you see to its fix:
| What you see | Likely cause | Go to |
|---|---|---|
| Only a "Posts by…" link | Script stripped or banned | Fix 2 |
| Old Likes or Moments embed | Retired timeline type | Fix 1 |
| Blank after page navigation | Single-page app | Fix 3 |
| Blank for some visitors | Privacy tools, protection | Fix 4 |
That bare link is what your visitors see when X's script never runs.
Fix 1: Use a timeline type X still embeds
X retired the Likes, Collections and Moments timelines on 13 January 2023, its developer docs say. If you embedded one of those, it will never load again.
Make your new code at publish.x.com from a profile or public List URL. To cap the feed, add data-tweet-limit to the link; X allows 1 to 20 posts.
Fix 2: Keep the script and allow X's domains
Two things stop the script before it runs:
- Your editor strips it. Search your live page's source for
widgets.js. In WordPress, saving scripts takes theunfiltered_htmlcapability, which only Administrators and Editors have on a single site. - Your Content Security Policy blocks it. A CSP is a header that lists where your page may load scripts and frames from, and a blocked load shows in the browser console.
We read X's widgets.js on 22 October 2026. platform.x.com redirects to platform.twitter.com, and the timeline loads in a frame from syndication.twitter.com. Add these to your policy:
script-src https://platform.x.com https://platform.twitter.com
frame-src https://platform.twitter.com https://syndication.twitter.com
Fix 3: Re-run the widget in single-page apps
X's script scans your page once, when it loads. In React, Next.js or Vue, a timeline your app adds later stays a plain link.
X's docs say content added later needs twttr.widgets.load(). Call it after your link renders:
// after the <a class="twitter-timeline"> tag is in the DOM
window.twttr?.widgets?.load();
Fix 4: Check the account and the visitor's browser
Some blanks are not yours to fix:
- Protected accounts. X's Help Center says they "are not compatible with any X embedded timeline widgets."
- Privacy tools. Mozilla says Firefox blocks "the most common trackers from Facebook, Twitter and LinkedIn," and blocked content may not appear.
- Private windows. Firefox blocks that content there by default, so test in a normal window with extensions off.
You can't change a visitor's settings. You can make the fallback link useful, like "Read our latest posts on X."
Embed a Twitter timeline on your website without X's script
The sure fix is to stop loading X's script on your pages. This Node script asks Weir's API for an account's recent posts, caches the JSON and writes plain HTML.
You need Node 22, a free Weir key, and a server that runs cron. Keep the key there, because any key in browser JavaScript is public. Our guide to getting tweets in JavaScript covers paging.
Step 1: Fetch and cache the posts
GET /v1/x/users/{user}/posts returns posts and reposts, newest first, with any pinned post on top. Your cache is written only when a call succeeds:
// x-feed.mjs Run: node --env-file=.env x-feed.mjs github
import { readFile, writeFile } from "node:fs/promises";
const user = process.argv[2] ?? "github";
const cache = `${user}-posts.json`;
async function refresh() {
if (!process.env.WEIR_API_KEY) throw new Error("WEIR_API_KEY is not set");
const url = `https://api.weirapp.io/v1/x/users/${encodeURIComponent(user)}/posts?limit=10`;
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.WEIR_API_KEY}` },
});
if (!res.ok) throw new Error(`Weir returned ${res.status}`);
await writeFile(cache, await res.text());
}
Step 2: Render escaped HTML
escape turns <, & and quotes into entities, so a post can never inject markup into your page:
const ENTITIES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
const escape = (s) => s.replace(/[&<>"']/g, (c) => ENTITIES[c]);
const day = (iso) =>
new Date(iso).toLocaleDateString("en-GB", { dateStyle: "medium", timeZone: "UTC" });
const item = (p) =>
` <li><p>${escape(p.text).replaceAll("\n", "<br>")}</p>` +
` <a href="${escape(p.url)}"><time datetime="${p.createdAt}">${day(p.createdAt)}</time></a></li>`;
function render(posts) {
const items = posts
.filter((p) => !p.retweetedPost) // drop reposts
.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) // pinned post back in date order
.map(item);
return `<ul class="x-feed">\n${items.join("\n")}\n</ul>\n`;
}
Step 3: Write the file, even when the API fails
A failed refresh is logged, and the last good cache is rendered, so your feed never goes blank. --cached skips the API:
if (!process.argv.includes("--cached")) {
await refresh().catch((err) => console.error(`${err.message}: keeping the last good cache`));
}
const { data } = JSON.parse(await readFile(cache, "utf8"));
await writeFile("x-feed.html", render(data));
console.log(`wrote x-feed.html from ${cache}`);
Put WEIR_API_KEY=... in a .env file beside the script, then add a cron line for every 6 hours:
0 */6 * * * cd /srv/x-feed && node --env-file=.env x-feed.mjs github >> feed.log 2>&1
What the feed looks like
We called the endpoint for @github with limit=3 on 22 October 2026, for 6 credits.
GitHub's pinned post from 7 October came first, with no pinned flag, so the script sorts by date. The third item was a repost, with likeCount: 0 and its real counts in retweetedPost.
On that response, the script wrote this (text trimmed):
<ul class="x-feed">
<li><p>1 week away from #GitHubUniverse and we can't wait to see you 🌁<br><br>...</p> <a href="https://x.com/github/status/2113057501985796394"><time datetime="2026-10-21T23:59:01.000Z">21 Oct 2026</time></a></li>
<li><p>📣 Coming soon to GitHub Copilot: intelligent local model routing. ...</p> <a href="https://x.com/github/status/2107896916595884177"><time datetime="2026-10-07T18:12:42.000Z">7 Oct 2026</time></a></li>
</ul>
Include the file where your old widget sat, with a server-side include, a template partial or a build step. Style the <ul> with your own CSS.
What does a self-hosted Twitter feed widget cost?
Each refresh you run pays for the call and a profile lookup, then 1 credit per post. At 10 posts, that's 13 credits at most.
Weir's free grant is 5,000 credits a month, so your 6-hour schedule fits easily.
If you refresh hourly, you go over by 4,360 credits. At the $5 pack's rate, that's about 87 cents a month.
You pay nothing for failed calls. The X API cost calculator prices other schedules.
When not to use Weir
- X's widget works for your visitors. It's free and looks like a timeline on x.com.
- You need posts within minutes. A 6-hour cache can be 6 hours old.
- You won't run a server or cron. A hosted feed widget service fits better.
- Price per post is all that matters. TwitterAPI.io lists $0.15 per 1,000 tweets as of October 2026.
Weir reads public data only and can't post. Check X's terms for your use; this isn't legal advice.
FAQ
Why is my X embed timeline not working?
Usually your script never ran. Your editor stripped it, your security policy blocked it, or your app added the link after the script loaded. View your page source for widgets.js, then check the browser console for errors.
Did X deprecate embedded timelines?
Not Profile or List timelines, as of 22 October 2026. X's docs still describe both, and publish.x.com still gives you the code. X retired only Likes, Collections and Moments timelines, in January 2023.
How do I embed a Twitter feed on my website for free?
Make the code at publish.x.com and paste it into a raw HTML block. For a feed without X's script, the Node script above runs every 6 hours inside Weir's free monthly credits.
Can I still embed a single tweet?
Yes. On 22 October 2026, publish.x.com still returned single-post embed code for a NASA post. Paste the post's URL there and copy the code into a raw HTML block.
Sources
All checked on 22 October 2026:
- X docs, embedded timelines: https://docs.x.com/x-for-websites/timelines/overview
- X docs, loading widgets: https://docs.x.com/x-for-websites/javascript-api/guides/scripting-loading-and-initialization
- X Help Center, embed a timeline (Wayback, 19 May 2026): https://web.archive.org/web/20260519024649/https://help.x.com/en/using-x/embed-x-feed
- publish.x.com embed code for @NASA: https://publish.x.com/oembed?url=https://x.com/NASA
- X's widget script: https://platform.x.com/widgets.js
- X's timeline frame for @NASA, logged out: https://syndication.twitter.com/srv/timeline-profile/screen-name/NASA
- Mozilla, trackers Firefox blocks (Wayback, 21 May 2026): https://web.archive.org/web/20260521122814/https://support.mozilla.org/en-US/kb/trackers-and-scripts-firefox-blocks-enhanced-track
- WordPress, roles and capabilities: https://wordpress.org/documentation/article/roles-and-capabilities/
- University of Cape Town notice (21 July 2023): https://uct.ac.za/staff/articles/2023-07-21-twitter-timeline-embed-issue
- ShortPoint known issue (15 May 2024): https://support.shortpoint.com/support/solutions/articles/1000318706-known-issue-x-timeline-displays-nothing-to-see-here-yet-
- TwitterAPI.io pricing: https://twitterapi.io/pricing
- Weir billing: https://docs.weirapp.io/billing
- Our @github timeline call through Weir, 22 October 2026