🚚 Installation

npm install remark-tweet-card

or

yarn add remark-tweet-card

🔦 Usage

  1. Import

    Same as any remark plugin. Here’s an example with Astro:

    // astro.config.js
    import { unified } from '@astrojs/markdown-remark';
    import remarkTweetCard from 'remark-tweet-card';
    
    export default defineConfig({
    	markdown: {
    		processor: unified({
    			remarkPlugins: [remarkTweetCard],
    		}),
    	},
    });
  2. Import global styles

    @import 'remark-tweet-card/style.css';
  3. Use in Markdown

    [$tweet](1654079444816936969)
    
    or
    
    [$tweet](https://x.com/github/status/1654079444816936969)

    It renders as a full tweet card including avatar, body, media, quoted tweets, engagement metrics, and more.

    The $tweet prefix in the link text marks the tweet card. It can be customized via the delimiter option.


🔧 Configuration

remarkTweetCard({
	// CSS class prefix (default: 'tweet-card')
	prefix: 'tweet-card',

	// API request timeout in milliseconds (default: 10000)
	timeout: 10000,

	// Marker text used in the Markdown link (default: '$tweet')
	delimiter: '$tweet',

	// Custom tweet data fetcher
	fetchTweet: async (id, { timeout }) => {
		/* ... */
	},

	// Custom full tweet card HTML renderer
	renderTweet: (tweet, { prefix, text }) => {
		/* return HTML string */
	},

	// Custom error fallback HTML renderer
	renderError: (url, { prefix, text }) => {
		/* return HTML string */
	},

	// Localized text labels
	text: {
		replies: 'Replies',
		reposts: 'Reposts',
		quotes: 'Quotes',
		likes: 'Likes',
    replyingTo: 'Replying to',
		viewOnX: 'View on X',
		notFound: 'Tweet not available.',
	},
});

Localization

Use the text option to localize the tweet card UI into any language. For example, configure it for Chinese:

remarkTweetCard({
	text: {
		replies: '回复',
		reposts: '转发',
		quotes: '引用',
		likes: '喜欢',
		replyingTo: '回复',
		viewOnX: '在 X 上查看',
		notFound: '推文不可用。',
	},
});

🎨 Custom Styles

A default stylesheet is provided, with all colors controlled via CSS custom properties. Override the following variables to customize the appearance:

VariableDescriptionDefault (light)
--tc-bgCard background#ffffff
--tc-borderBorder color#cfd9de
--tc-textPrimary text color#0f1419
--tc-text-mutedMuted text color#536471
--tc-linkLink color#1d9bf0
--tc-primaryPrimary color (buttons, hover)#1d9bf0
--tc-verifiedVerified badge color#1d9bf0
--tc-font-familyFont family-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif
--tc-overlayVideo play button backgroundrgba(0,0,0,0.65)
--tc-hover-bgHover backgroundrgba(29,155,240,0.1)

Dark Mode

Dark styles are not included by default. Configure them based on your project’s needs:

@media (prefers-color-scheme: dark) {
	:root {
		--tc-bg: #16181c;
		--tc-border: #2f3336;
		--tc-text: #e7e9ea;
		--tc-text-muted: #71767b;
	}
}

or

.dark {
	--tc-bg: #16181c;
	--tc-border: #2f3336;
	--tc-text: #e7e9ea;
	--tc-text-muted: #71767b;
}

😎 Advanced Usage

Custom Data Fetching

If you need to proxy requests or add extra error handling:

import remarkTweetCard from 'remark-tweet-card';
import { fetchTweetData } from 'remark-tweet-card/api';

function myFetcher(id, { timeout }) {
  // Fetch data through your own proxy
  const res = await fetch(`/api/tweet?id=${id}`);
  return res.ok ? res.json() : null;
}

remarkTweetCard({ fetchTweet: myFetcher })

Custom Rendering

Take full control of the HTML output:

remarkTweetCard({
	renderTweet(tweet, { prefix, text }) {
		// Generate HTML with your own template
		return `<blockquote class="${prefix}">${tweet.text}</blockquote>`;
	},
});

Reusing Submodules

The plugin’s internal modules are also exported individually for easy reuse:

import {
	extractTweetId,
	fetchTweetData,
	clearCache,
} from 'remark-tweet-card/api';
import { buildTweetHTML, buildErrorHTML } from 'remark-tweet-card/html';
import { formatCount } from 'remark-tweet-card/utils';

const id = extractTweetId('https://x.com/user/status/123456');
const tweet = await fetchTweetData(id, { timeout: 5000 });
const html = tweet ? buildTweetHTML(tweet, { prefix: 'tweet-card' }) : '';