🔗 Share Embed

Content Tools src
281 lines by @nichxbt

Share & Embed Posts on X - by nichxbt

How to Use

  1. Navigate to x.com and log in
  2. Open DevTools Console (F12 or Cmd+Option+I)
  3. Paste the script below and press Enter

Configuration Options

OptionDefaultDescription
mode'bulkCopy''copyLinks' = copy link of specific posts
embedUrl''URL of the post to embed
maxPosts50Max posts to collect links for
maxScrollAttempts20Max scroll attempts before stopping

Default Configuration

const CONFIG = {
    // ── Mode: 'copyLinks', 'embedSingle', or 'bulkCopy' ──
    mode: 'bulkCopy',           // 'copyLinks' = copy link of specific posts
                                // 'embedSingle' = get embed code for a post
                                // 'bulkCopy' = scroll timeline and collect all post links

    // ── For 'copyLinks' mode ──
    postUrls: [
      // 'https://x.com/nichxbt/status/123456789',
    ],

    // ── For 'embedSingle' mode ──
    embedUrl: '',               // URL of the post to embed

    // ── For 'bulkCopy' mode ──
    maxPosts: 50,               // Max posts to collect links for
    maxScrollAttempts: 20,      // Max scroll attempts before stopping

    // ── Timing ──
    minDelay: 1000,
    maxDelay: 2000,
    scrollDelay: 2000,
  };

Full Script

Copy and paste this entire script into your browser DevTools console on x.com.

// Share & Embed Posts on X - by nichxbt
// https://github.com/nirholas/xactions
// Copy post links, get embed codes, or bulk copy links from timeline
// 1. Go to x.com (timeline, profile, or search results)
// 2. Open Developer Console (F12)
// 3. Edit CONFIG below
// 4. Paste and run
//
// Last Updated: 30 March 2026
(() => {
  'use strict';

  const CONFIG = {
    // ── Mode: 'copyLinks', 'embedSingle', or 'bulkCopy' ──
    mode: 'bulkCopy',           // 'copyLinks' = copy link of specific posts
                                // 'embedSingle' = get embed code for a post
                                // 'bulkCopy' = scroll timeline and collect all post links

    // ── For 'copyLinks' mode ──
    postUrls: [
      // 'https://x.com/nichxbt/status/123456789',
    ],

    // ── For 'embedSingle' mode ──
    embedUrl: '',               // URL of the post to embed

    // ── For 'bulkCopy' mode ──
    maxPosts: 50,               // Max posts to collect links for
    maxScrollAttempts: 20,      // Max scroll attempts before stopping

    // ── Timing ──
    minDelay: 1000,
    maxDelay: 2000,
    scrollDelay: 2000,
  };

  // ── Selectors ──
  const SEL = {
    tweet:       'article[data-testid="tweet"]',
    share:       '[data-testid="share"]',
    copyLink:    '[data-testid="copyLink"]',
    embedTweet:  '[data-testid="embedTweet"]',
    menuItem:    '[role="menuitem"]',
    tweetText:   '[data-testid="tweetText"]',
  };

  // ── Utilities ──
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
  const randomDelay = () => Math.floor(Math.random() * (CONFIG.maxDelay - CONFIG.minDelay + 1)) + CONFIG.minDelay;

  const waitForElement = async (selector, timeout = 10000) => {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      const el = document.querySelector(selector);
      if (el) return el;
      await sleep(200);
    }
    return null;
  };

  const extractPostUrl = (tweetEl) => {
    // Find the timestamp link which contains the post URL
    const timeLink = tweetEl.querySelector('a[href*="/status/"] time')?.closest('a');
    if (timeLink) {
      return 'https://x.com' + timeLink.getAttribute('href');
    }
    return null;
  };

  // ── Copy Link via Share Menu ──
  const copyLinkForTweet = async (tweetEl) => {
    const shareBtn = tweetEl.querySelector(SEL.share);
    if (!shareBtn) {
      console.warn('⚠️  Share button not found on tweet.');
      return false;
    }

    shareBtn.click();
    await sleep(800);

    // Click "Copy link" from the share menu
    const copyBtn = await waitForElement(SEL.copyLink, 3000);
    if (copyBtn) {
      copyBtn.click();
      await sleep(500);
      return true;
    }

    // Fallback: look for menu item with "Copy link" text
    const menuItems = document.querySelectorAll(SEL.menuItem);
    for (const item of menuItems) {
      if (item.textContent.toLowerCase().includes('copy link')) {
        item.click();
        await sleep(500);
        return true;
      }
    }

    console.warn('⚠️  "Copy link" option not found in share menu.');
    return false;
  };

  // ── Mode: Copy Links for Specific Posts ──
  const copyLinks = async () => {
    if (CONFIG.postUrls.length === 0) {
      console.error('❌ Please add post URLs to CONFIG.postUrls.');
      return;
    }

    console.log(`🔄 Copying links for ${CONFIG.postUrls.length} posts...`);
    const results = [];

    for (const url of CONFIG.postUrls) {
      console.log(`   📋 ${url}`);
      results.push(url);
    }

    // Copy all URLs to clipboard
    const text = results.join('\n');
    try {
      await navigator.clipboard.writeText(text);
      console.log(`✅ ${results.length} links copied to clipboard!`);
    } catch (e) {
      console.log('⚠️  Clipboard access denied. Here are your links:');
      console.log(text);
    }

    return results;
  };

  // ── Mode: Get Embed Code ──
  const embedSingle = async () => {
    if (!CONFIG.embedUrl) {
      console.error('❌ Please set CONFIG.embedUrl to the post URL.');
      return;
    }

    console.log('🔄 Generating embed code...');

    // Navigate to the post if needed
    const statusPath = CONFIG.embedUrl.replace('https://x.com', '');
    if (!window.location.pathname.includes(statusPath)) {
      console.log('🔄 Navigating to post...');
      window.location.href = CONFIG.embedUrl;
      await sleep(3000);
    }

    // Click share button on the tweet
    const tweet = await waitForElement(SEL.tweet);
    if (!tweet) {
      console.error('❌ Could not find the tweet.');
      return;
    }

    const shareBtn = tweet.querySelector(SEL.share);
    if (!shareBtn) {
      console.error('❌ Share button not found.');
      return;
    }
    shareBtn.click();
    await sleep(1000);

    // Look for "Embed post" option
    const embedBtn = await waitForElement(SEL.embedTweet, 3000);
    if (embedBtn) {
      embedBtn.click();
      await sleep(2000);
      console.log('✅ Embed dialog opened!');
      console.log('💡 You can also get the embed code directly from:');
    } else {
      // Fallback: look in menu items
      const menuItems = document.querySelectorAll(SEL.menuItem);
      let found = false;
      for (const item of menuItems) {
        if (item.textContent.toLowerCase().includes('embed')) {
          item.click();
          found = true;
          await sleep(2000);
          console.log('✅ Embed dialog opened!');
          break;
        }
      }
      if (!found) {
        console.log('⚠️  Embed option not found in share menu.');
      }
    }

    // Provide the publish.twitter.com URL as fallback
    const embedDirectUrl = `https://publish.twitter.com/?url=${encodeURIComponent(CONFIG.embedUrl)}`;
    console.log(`🔗 Direct embed URL: ${embedDirectUrl}`);
    console.log('💡 Open the URL above to get the full embed HTML code.');

    return embedDirectUrl;
  };

  // ── Mode: Bulk Copy Links from Timeline ──
  const bulkCopy = async () => {
    console.log(`🔄 Collecting up to ${CONFIG.maxPosts} post links from timeline...`);

    const collectedUrls = new Set();
    let scrollAttempts = 0;
    let lastCount = 0;

    while (collectedUrls.size < CONFIG.maxPosts && scrollAttempts < CONFIG.maxScrollAttempts) {
      const tweets = document.querySelectorAll(SEL.tweet);

      for (const tweet of tweets) {
        if (collectedUrls.size >= CONFIG.maxPosts) break;

        const url = extractPostUrl(tweet);
        if (url && !collectedUrls.has(url)) {
          collectedUrls.add(url);
        }
      }

      if (collectedUrls.size === lastCount) {
        scrollAttempts++;
      } else {
        scrollAttempts = 0;
        lastCount = collectedUrls.size;
      }

      console.log(`   📋 Collected ${collectedUrls.size} links so far...`);

      // Scroll down to load more
      window.scrollBy(0, window.innerHeight * 2);
      await sleep(CONFIG.scrollDelay);
    }

    const urls = Array.from(collectedUrls);
    const text = urls.join('\n');

    // Try to copy to clipboard
    try {
      await navigator.clipboard.writeText(text);
      console.log(`✅ ${urls.length} links copied to clipboard!`);
    } catch (e) {
      console.log('⚠️  Clipboard access denied. Outputting links below:');
    }

    // Also store in sessionStorage
    sessionStorage.setItem('xactions_bulk_links', JSON.stringify(urls));
    console.log('💾 Links saved to sessionStorage (key: "xactions_bulk_links")');

    // Log all links
    console.log('\n📋 Collected Links:');
    urls.forEach((url, i) => console.log(`   ${i + 1}. ${url}`));

    return urls;
  };

  // ── Main ──
  const run = async () => {
    console.log('═══════════════════════════════════════');
    console.log('🔗 XActions — Share & Embed Posts');
    console.log('═══════════════════════════════════════');

    let result;
    switch (CONFIG.mode) {
      case 'copyLinks':
        result = await copyLinks();
        break;
      case 'embedSingle':
        result = await embedSingle();
        break;
      case 'bulkCopy':
        result = await bulkCopy();
        break;
      default:
        console.error(`❌ Invalid mode: "${CONFIG.mode}". Use 'copyLinks', 'embedSingle', or 'bulkCopy'.`);
    }

    console.log('═══════════════════════════════════════');
    console.log('🏁 Done! — by nichxbt');

    return result;
  };

  run();
})();

⚡ More XActions Scripts

Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.

Browse All Scripts