• Townie
    AI
  • Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
nbbaier

nbbaier

react-tweet-vt

Public
Like
react-tweet-vt
Home
Code
9
src
6
.vtignore
AGENTS.md
README.md
biome.json
deno.json
H
example.tsx
package.json
tailwind-theme.html
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
…
README.md

react-tweet-valtown

A Val Town-optimized version of react-tweet for server-side rendering in Deno environments without bundlers.

Features

  • ✅ No Build Step Required - Works directly in Val Town's Deno runtime
  • ✅ Tailwind v4 Styling - Uses Tailwind CDN for beautiful, responsive tweets
  • ✅ Server-Side Only - Optimized for Val Town's server-side JSX rendering
  • ✅ Full TypeScript Support - Complete type definitions included
  • ✅ Twitter API Integration - Fetches tweets from Twitter's syndication API
  • ✅ Dark Mode Support - Automatic dark mode via Tailwind
  • ✅ Verified Badges - Shows verification status
  • ✅ Media Support - Images, videos, and GIFs
  • ✅ Quoted Tweets - Displays quoted tweets inline
  • ✅ Reply Threads - Shows reply context

Quick Start

1. Create a new HTTP Val in Val Town

Create val
/** @jsxImportSource npm:react */ import { Tweet } from "./react-tweet-valtown/src/index.ts"; export default async function(req: Request) { const url = new URL(req.url); const tweetId = url.searchParams.get("id") || "1683899539984359424"; return new Response( <html> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Tweet {tweetId}</title> {/* Required: Tailwind v4 CDN */} <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> </head> <body> <Tweet id={tweetId} /> </body> </html>, { headers: { "Content-Type": "text/html; charset=utf-8" } } ); }

2. That's it! 🎉

Your tweet will be fetched and rendered server-side with full styling.

Usage

Basic Tweet

Create val
<Tweet id="1683899539984359424" />

Multiple Tweets

Create val
<div> <Tweet id="1683899539984359424" /> <Tweet id="1234567890" /> </div>

Custom Fallback

Create val
<Tweet id="1683899539984359424" fallback={<div>Loading tweet...</div>} />

Error Handling

Create val
<Tweet id="1683899539984359424" onError={(error) => console.error("Failed to load tweet:", error)} />

Custom Components

Create val
<Tweet id="1683899539984359424" components={{ TweetNotFound: () => <div>This tweet could not be found</div>, AvatarImg: (props) => <img {...props} loading="lazy" />, MediaImg: (props) => <img {...props} loading="lazy" /> }} />

API

<Tweet>

Main component for rendering tweets.

Props:

PropTypeDefaultDescription
idstringRequiredThe tweet ID to fetch and render
componentsTwitterComponentsundefinedCustom component overrides
fetchOptionsRequestInitundefinedCustom fetch options for the Twitter API
fallbackJSX.Element<TweetSkeleton />Fallback component while loading
onError(error: any) => anyundefinedError handler callback

fetchTweet(id: string, fetchOptions?: RequestInit)

Fetches a tweet from Twitter's syndication API.

Create val
import { fetchTweet } from "./react-tweet-valtown/src/index.ts"; const result = await fetchTweet("1683899539984359424"); if (result.data) { console.log(result.data); }

Returns:

{ data?: Tweet; // Tweet data if found tombstone?: true; // True if tweet is private notFound?: true; // True if tweet doesn't exist }

getTweet(id: string, fetchOptions?: RequestInit)

Wrapper around fetchTweet that returns just the tweet data.

Create val
import { getTweet } from "./react-tweet-valtown/src/index.ts"; const tweet = await getTweet("1683899539984359424"); if (tweet) { console.log(tweet.text); }

Returns: Promise<Tweet | undefined>

enrichTweet(tweet: Tweet)

Enriches a tweet with additional computed properties.

Create val
import { enrichTweet, getTweet } from "./react-tweet-valtown/src/index.ts"; const tweet = await getTweet("1683899539984359424"); if (tweet) { const enriched = enrichTweet(tweet); console.log(enriched.url); // https://x.com/user/status/123... console.log(enriched.user.url); // https://x.com/user console.log(enriched.like_url); // https://x.com/intent/like?tweet_id=123... }

Utility Functions

Create val
import { formatNumber, formatDate, getMediaUrl } from "./react-tweet-valtown/src/index.ts"; formatNumber(1234567); // "1.2M" formatNumber(1234); // "1.2K" const date = new Date("2024-01-15T12:00:00Z"); formatDate(date); // "12:00 PM · Jan 15, 2024" getMediaUrl(media, "small"); // Formatted media URL getMediaUrl(media, "medium"); getMediaUrl(media, "large");

Styling

Default Styling

The package uses Tailwind v4 for styling. Include the CDN in your HTML:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Dark Mode

Dark mode is automatically supported via Tailwind's dark mode:

<html class="dark"> <!-- Tweets will use dark mode colors --> </html>

Or use system preference:

<!-- Automatically uses user's system preference --> <html> <head> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> </head> <body> <Tweet id="123" /> </body> </html>

Custom Theme

Customize colors using Tailwind's theme configuration:

<style type="text/tailwindcss"> @theme { --color-tweet-blue: #your-color; --color-tweet-red: #your-color; } </style>

Custom CSS

Add your own styles:

<style> .react-tweet { /* Override tweet container styles */ max-width: 700px; } </style>

Components

All components are exported and can be used individually:

Create val
import { Tweet, // Main component EmbeddedTweet, // Tweet without data fetching TweetContainer, // Container wrapper TweetHeader, // Author info and avatar TweetBody, // Tweet text TweetMedia, // Images/videos TweetActions, // Like, reply buttons TweetInfo, // Date and metadata TweetInReplyTo, // Reply context TweetReplies, // Reply count TweetNotFound, // Error state TweetSkeleton, // Loading state QuotedTweet, // Quoted tweet component } from "./react-tweet-valtown/src/index.ts";

Custom Composition

Create val
import { getTweet, enrichTweet, EmbeddedTweet } from "./react-tweet-valtown/src/index.ts"; export default async function() { const tweet = await getTweet("123"); if (!tweet) return <div>Not found</div>; return <EmbeddedTweet tweet={tweet} />; }

TypeScript

Full TypeScript support with comprehensive type definitions:

Create val
import type { Tweet, EnrichedTweet, TweetUser, TwitterComponents, TweetProps, } from "./react-tweet-valtown/src/index.ts"; const customComponent: TwitterComponents = { TweetNotFound: () => <div>Custom not found</div> };

Differences from Original react-tweet

Featurereact-tweetreact-tweet-valtown
Build StepRequired (SWC/Webpack)Not required ✅
StylingCSS ModulesTailwind v4 CDN
RuntimeNode.js/BrowserDeno (Val Town)
Client-SideSupported (SWR)Not supported ⚠️
Server-SideSupported (RSC)Supported ✅
Import SyntaxStandard npmnpm: prefix for npm packages
BundlerVite/Next/WebpackNone required

Not Supported

  • ❌ Client-side rendering - Val Town's JSX is server-only
  • ❌ SWR variant - No useTweet() hook
  • ❌ Interactive features - No onClick handlers or client state
  • ❌ Custom fonts loading - Use CDN fonts instead

Examples

Basic Example

See example.tsx for a complete working example.

With Dark Mode Toggle

Create val
export default async function(req: Request) { const url = new URL(req.url); const theme = url.searchParams.get("theme") || "light"; return new Response( <html className={theme}> <head> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> </head> <body> <Tweet id="123" /> <a href="?theme=dark">Dark</a> | <a href="?theme=light">Light</a> </body> </html>, { headers: { "Content-Type": "text/html; charset=utf-8" } } ); }

With Custom Styling

Create val
export default async function() { return new Response( <html> <head> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> <style type="text/tailwindcss"> {` @theme { --color-tweet-blue: #8b5cf6; /* Purple instead of blue */ } `} </style> </head> <body className="bg-gradient-to-br from-purple-100 to-pink-100 min-h-screen p-8"> <div className="max-w-2xl mx-auto"> <h1 className="text-4xl font-bold mb-8">My Tweets</h1> <Tweet id="123" /> </div> </body> </html>, { headers: { "Content-Type": "text/html; charset=utf-8" } } ); }

Multiple Tweets from Search

Create val
export default async function(req: Request) { const tweetIds = [ "1683899539984359424", "1234567890", "9876543210", ]; return new Response( <html> <head> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> </head> <body> <div className="max-w-2xl mx-auto space-y-4 p-4"> {tweetIds.map(id => ( <Tweet key={id} id={id} /> ))} </div> </body> </html>, { headers: { "Content-Type": "text/html; charset=utf-8" } } ); }

Troubleshooting

Tweet not loading

Make sure the tweet ID is correct and the tweet is public. Private or deleted tweets will show the "not found" component.

Styling not working

Ensure you've included the Tailwind CDN script:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Import errors

Make sure you're using the correct import syntax for Val Town:

Create val
/** @jsxImportSource npm:react */ // Required at top of file import { Tweet } from "./react-tweet-valtown/src/index.ts"; // Local file

Type errors

Ensure your Val Town is using TypeScript mode. Add the JSX pragma at the top of your file.

Performance

  • Server-side rendering: Fast initial load, no client-side JavaScript needed for display
  • Tailwind CDN: ~50KB (cached across sites)
  • Tweet data: Fetched once per render from Twitter's CDN
  • Caching: Implement your own caching layer if needed

Recommended Caching

Create val
const tweetCache = new Map(); export default async function(req: Request) { const id = new URL(req.url).searchParams.get("id") || "123"; if (!tweetCache.has(id)) { const tweet = await getTweet(id); tweetCache.set(id, tweet); } const tweet = tweetCache.get(id); // ... render tweet }

Contributing

This is an adaptation of the original react-tweet package. Please report Val Town-specific issues in this repository.

License

MIT - Same as the original react-tweet package.

Credits

  • Original package: react-tweet by Vercel
  • Adapted for Val Town by converting CSS Modules to Tailwind v4
  • Twitter API integration maintained from original

Links

  • Val Town
  • Original react-tweet
  • Tailwind CSS v4
  • Deno
FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.