Extremely fast static renderer for tweets.
Visit react-static-tweets.vercel.app and append your tweet ID. You can also append /dynamic/<tweetId>
if you want to test the non-SSR version.
Twitter's embedding SDK is horribly slow and inefficient. For embedding tweets on your site (including SSR), this solution is significantly more performant. 🔥
This project takes Vercel's work on static tweet rendering and packages it up into two easy-to-use NPM packages.
- ⚡ Fast - 10-100x faster than using Twitter's iframe embedding.
- 🔥 Solid - Used in production by super.so, react-notion-x, and others.
- 🚀 Simple - TypeScript + React.
npm install react-static-tweets static-tweets date-fns
# or
yarn add react-static-tweets static-tweets date-fns
Note: this project currently only works with Next.js (see #2 for more info).
You'll need to pre-fetch tweet data server-side using fetchTweetAst
and then render it using the Tweet
component.
import React from 'react'
import { fetchTweetAst } from 'static-tweets'
import { Tweet } from 'react-static-tweets'
const tweetId = '1358199505280262150'
export const getStaticProps = async () => {
try {
const tweetAst = await fetchTweetAst(tweetId)
return {
props: {
tweetId,
tweetAst
},
revalidate: 10
}
} catch (err) {
console.error('error fetching tweet', err)
throw err
}
}
export default function Example({ tweetId, tweetAst }) {
return <Tweet id={tweetId} ast={tweetAst} />
}
If you have multiple tweets, then we recommend using the built-in TwitterContextProvider
to store a map from tweet ID to tweet AST. Here's an example using this approach:
import React from 'react'
import pMap from 'p-map'
import { fetchTweetAst } from 'static-tweets'
import { Tweet, TwitterContextProvider } from 'react-static-tweets'
// NOTE: You'll likely infer your list of tweets by introspecting your page's
// content from a CMS.
const tweetIds = [
'1358199505280262150',
'1374492662061953034',
'1358199505280262150'
// ...
]
export const getStaticProps = async () => {
try {
// Fetch all tweet ASTs statically
const tweetAsts = await pMap(tweetIds, fetchTweetAst, {
concurrency: 4
})
// Create a map from tweet ID to tweet AST
const tweetAstMap = tweetIds.reduce((tweetId, map, index) => ({
...map,
[tweetId]: tweetAsts[index]
}))
return {
props: {
tweetAstMap
},
revalidate: 60
}
} catch (err) {
console.error('error fetching tweets', err)
throw err
}
}
export default function Example({ tweetAstMap }) {
return (
<TwitterContextProvider value={{ tweetAstMap }}>
{tweetIds.map((tweetId) => (
<div key={tweetId}>
{/*
There's no need to pass the tweet AST directly if it is provided via TwitterContextProvider. This is nice in situations where you're
rendering tweets in deeply nested component trees.
*/}
<Tweet id={tweetId} />
</div>
))}
</TwitterContextProvider>
)
}
You'll need to import some CSS styles as well. For Next.js, we recommend you put these in pages/_app
:
import 'react-static-tweets/styles.css'
Add pbs.twimg.com
to your next.config.js
since we use next/image
to load images.
module.exports = {
images: {
domains: ['pbs.twimg.com']
}
}
Here is an example Next.js project, with the most important code in pages/[tweetId].tsx
. You can view this example live on Vercel.
Package | NPM | Environment | Description |
---|---|---|---|
static-tweets | Node.js | Fetches tweet ASTs. | |
react-static-tweets | Browser + SSR | React renderer for tweets given an AST. |
react-static-tweets
is meant for rendering tweets as efficiently as possible. The Tweet
component assumes that you've already pre-fetched tweet AST data ahead of time, most likely during SSR.
Rendering dynamic tweets on the client-side is supported; however, you'll need to wrap fetchTweetAst
in an API route since it can't be used from the browser.
You can view an example of this in action via example/pages/dynamic/[tweetId].tsx
.
My main contribution is packaging the Vercel team's excellent work into two isolated packages: static-tweets
for server-side fetching of tweet ASTs and react-static-tweets
for client-side rendering as well as SSR.
- Inspired by this demo from the Vercel team
- And the underlying repo by Luis Alvarez
- Most of the core code is adapted from Guillermo Rauch's blog
- Converted the JS codebase to TypeScript
- Removed
styled-jsx
because using a flat CSS file (with a.static-tweet
class prefix) makes bundling for NPM easier - Fixed some minor formatting bugs
MIT © Travis Fischer
Support my OSS work by following me on twitter