Skip to content

zendegani/XClipper

Repository files navigation

XClipper

The high-fidelity X / Twitter web clipper — save posts, threads & articles to Markdown, PDF, HTML, JSON, CSV & Obsidian, one at a time or in batch.

CI License: PolyForm Noncommercial 1.0.0 Privacy: 100% local Chrome Manifest V3

Install from the Chrome Web Store

Why · Features · Install · Usage · For developers

XClipper extension popup — export X posts, threads, and articles to Markdown, PDF, or Obsidian

XClipper is a source-available Chrome extension that exports x.com content as Markdown, PDF, HTML, JSON, TXT, CSV, or Obsidian notes — one post at a time, or in batch from your bookmarks, a profile, your likes, or a hand-picked selection. It runs entirely in your browser: no X API key, no account, no server. Free for noncommercial use (commercial license required to sell or build a paid product on it).

Why XClipper

Most "tweet to markdown" tools run a post through a generic HTML→Markdown converter and stop there. XClipper is built differently, and it shows up in four places:

  • Output you won't have to clean up. The DOM is parsed into a typed Content AST before anything is rendered, so structure survives: nested threads, quote tweets, polls, link cards, and full long-form Articles (headings, lists, code blocks) all come through faithfully. t.co links are resolved to real URLs, emoji and @mentions stay intact, truncated posts are expanded, and engagement bars, follow prompts, and trackers are stripped.
  • Real PDFs, not screenshots. PDF export goes through Chrome's native print engine — selectable text, clickable links, embedded images, and full Unicode/emoji — so the result is an actual document, not a flattened image.
  • Batch, not one-at-a-time. Export your entire Bookmarks, Likes, a Profile, or a hand-picked Selection in a single background job, with progress, pause/resume/stop, and dedup.
  • 100% local, zero setup. No API keys, no accounts, nothing leaves your browser. Install and clip.

XClipper converts X posts, threads, and articles to Markdown, PDF, and Obsidian notes

Features

Headline

  • Batch export — Export many posts at once from four sources via an icon tab strip: Bookmarks, Profile (own posts; reposts skipped), Likes, or Selection (tick individual tweets with checkboxes on any timeline). Pick the format and whether to write Separate per-post files, one Combined file (x-compilation-<date>), or Both. Runs in the background (one job at a time) with a live progress bar and pause / resume / stop — close and reopen the popup mid-job. A dedup ledger skips already-exported items, and you can keep adding newly-scrolled posts to a running job.
  • Seven export formats — Save a single post as Markdown, PDF, HTML, JSON, TXT, or CSV, or hand it to Obsidian. Batch jobs support all of these except PDF. CSV pairs your metadata columns with a text column for the post body.
  • High-fidelity Markdown — Tweets, nested threads, quote tweets, polls, link cards, and X Articles render cleanly via the AST pipeline (no Turndown), with resolved t.co links, inlined emoji, and tidy @mentions.
  • True PDF export — Tweets, threads, and articles printed through Chrome's native engine: selectable text, clickable links, embedded images, full Unicode.
  • Obsidian integration — One-click handoff via the obsidian:// URI scheme with optional vault targeting, plus an Obsidian-friendly frontmatter schema ([[@handle]] wikilinks, synthesized title, published/created dates, prose description, and a customizable tags list with {-autocomplete).
  • Local image downloads — Save embedded X media next to your file to prevent link rot.

Also included

  • Three ways to trigger — Toolbar popup, an inline button on every tweet's action bar, and the right-click context menu.
  • Rich YAML frontmatter + field picker — Author, handle, date, source URL, content type, and engagement stats — with per-field toggles, saved separately for the default and Obsidian-friendly schemas.
  • Customizable filename template — Placeholders ({date}, {datetime}, {handle}, {author}, {id}, {slug}, {type}) with a live preview in Settings.
  • Single-tweet export — Grab one tweet without its thread via the context menu or by Shift/Alt-clicking the inline button.

Plus: copy-to-clipboard or download · optional inline engagement-stats row (💬 284 · 🔁 1.5K · ❤️ 8K · 🔖 253 · 👁 100K) · 12-language UI (extraction works in any language) · light & dark mode.

Inline button — one click on any tweet

Inline download button on a tweet's action bar

The download icon sits next to share on every tweet. One click opens the permalink in a new tab and exports it automatically. Toggle it to copy instead, and optionally close the tab once done.

Right-click context menu

Right-click context menu: save tweet as Markdown or PDF, copy, or add to Obsidian

Right-click anywhere on a tweet — body, image, or timestamp — and pick Save tweet as Markdown, Copy tweet as Markdown, or Add tweet to Obsidian. XClipper figures out which tweet you meant.

Settings — tune once, forget about it

XClipper popup and settings view side by side

The popup keeps per-export toggles (Save images locally, Show engagement stats inline, Include metadata) front and centre. The gear icon opens Settings, where set-once knobs live in four collapsible sections — Downloads, Obsidian, Frontmatter fields, and Inline button & context menu — persisted across sessions via chrome.storage.

Great For

  • Importing X content into Obsidian, Notion, Logseq, Hugo, or any Markdown-based PKM system
  • Exporting clean text for LLM prompts, RAG pipelines, or AI workflows
  • Archiving research threads, news references, and long-form articles offline
  • Building a searchable Second Brain from your Twitter/X activity
  • Preparing source material for writing, translation, or summarization

Install

From the Chrome Web Store

Install XClipper from the Chrome Web Store.

From source

git clone https://github.com/zendegani/xclipper.git
cd xclipper
npm install
npm run build

Then open chrome://extensions/ → enable Developer modeLoad unpacked → select dist/.

Usage

Pick whichever entry point you prefer — they all run the same extractor and respect the same toggles:

  • Toolbar popup — Click the XClipper icon, then Download .md, Copy .md, Export .pdf, or Add to Obsidian (more formats under the More formats row).
  • Inline button — Click the download icon on any tweet's action bar (and at the top of long-form articles). Shift/Alt-click to export just that tweet without its thread.
  • Right-click menu — Right-click any tweet and pick Save tweet as Markdown, Copy tweet as Markdown, or Copy just this tweet (no thread).

Per-export toggles live in the main popup; set-once options live under the gear icon. See Settings above.

Add to Obsidian tip: for long threads or content where you want images permanently archived, use Download .md with Save images locally and drag the resulting folder into your vault. The Obsidian deeplink is best for quick capture — it has an OS-level URL-length limit and leaves images as remote URLs.

Filenames default to @handle-tweetId.md (tweets/threads) or @handle-article-slug.md (articles), and are fully configurable via the filename template.

Current Limitations

  • Videos and GIFs are not exported as playable media files
  • Requires a page reload if the extension was installed or updated after the tab was opened
  • Some content may stop working if x.com changes its page structure significantly

For developers

How it works

  • Content script auto-injects on x.com/*/status/* pages.
  • DOM is parsed into a typed, JSON-serializable Content AST (the single source of truth); separate renderers turn it into Markdown or PDF — see docs/adr/0001.
  • Tweets/threads: custom AST rendering (t.co resolution, emoji inlining, @mention cleanup).
  • Articles: manual Draft.js block parsing for precise heading/list/code-block extraction.
  • PDF: the same AST renders to HTML and prints via Chrome's native engine.
  • DOM is cleaned (engagement bars, follow buttons, navigation) before extraction.
  • Downloads go through the chrome.downloads API after the background worker validates the message sender and sanitizes paths. Local image downloads are limited to expected X media hosts; external image URLs stay as remote Markdown links.
  • Nothing leaves your browser.

Tech stack

  • TypeScript + esbuild (content IIFE, background ESM)
  • Content AST → custom Markdown / PDF renderers (no Turndown)
  • Manifest V3

Project structure

xclipper/
├── src/
│   ├── content/        # DOM → Content AST (dom-to-ast/), inline button, PDF trigger
│   ├── ast/            # Content AST types + renderers (Markdown, PDF HTML)
│   ├── background/     # Service worker (downloads, context menu, PDF print, batch)
│   ├── popup/          # Popup UI, split: dom / settings-form / actions / widgets
│   ├── print/          # Print page for native PDF export
│   ├── shared/         # Cross-context logic (post-process, settings, media, obsidian)
│   ├── types/          # Shared TypeScript interfaces (messages)
│   ├── icons/          # Extension icons (16, 32, 48, 128px)
│   ├── _locales/       # i18n translations (en, es, de, fr, it, ja, pt_BR, ru, zh_CN, ar, fa, hi)
│   └── manifest.json   # Chrome MV3 manifest
├── dist/               # Build output (load this in Chrome)
├── docs/               # architecture.md overview + ADR + Content AST schema
├── tests/              # Vitest + JSDOM extractor/AST snapshot tests
├── build.mjs           # esbuild build script
├── package.json
└── tsconfig.json

Development

npm install        # Install dependencies
npm run build      # Build for production
npm run watch      # Build + watch for changes
npm test           # Run extractor snapshot tests (Vitest + JSDOM)
npm run package    # Package for Chrome Web Store (.zip)
npm run clean      # Clean build output

Tests

tests/extractor.test.ts runs the extractor against saved HTML fixtures and compares output against versioned .md snapshots (volatile frontmatter fields like likes and date are normalized). HTML fixtures are gitignored — capture them locally via copy(document.documentElement.outerHTML) in DevTools after the page is fully loaded. If no local fixtures are present, the suite keeps a passing baseline so fresh checkouts can still run tests.

Permissions

Permission Why
activeTab Read the current page's DOM when you click
downloads Save the .md file and allowed X media images to Downloads
storage Remember your popup toggle preferences and the optional Obsidian vault name
contextMenus Add Save / Copy tweet as Markdown to the right-click menu (X.com only)
host (X.com) Inject a content script on X.com to extract post / article content and draw the inline download button

Your data never leaves your device. No data is collected, transmitted, or stored externally. See PRIVACY.md.

License

XClipper is source-available under the PolyForm Noncommercial License 1.0.0 — not an OSI "open source" license.

  • Free for any noncommercial use: personal, research, education, nonprofits, hobby projects.
  • Commercial use requires a paid license. To sell XClipper (or a derivative), bundle it in a paid product, or otherwise use it commercially, contact the author to arrange a commercial license: @zendegani.

Copyright © 2026 Ali Zendegani.