/react-static-tweets

Extremely fast static renderer for tweets.

Primary LanguageTypeScriptMIT LicenseMIT

React Static Tweets

React Static Tweets

Extremely fast static renderer for tweets.

NPM Build Status Prettier Code Formatting

Demo

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.

Why?

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.

Features

  • 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.

Install

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).

Usage

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} />
}

Advanced Usage

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>
  )
}

Styles

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'

Next.js Config

Add pbs.twimg.com to your next.config.js since we use next/image to load images.

module.exports = {
  images: {
    domains: ['pbs.twimg.com']
  }
}

Next.js Example

Here is an example Next.js project, with the most important code in pages/[tweetId].tsx. You can view this example live on Vercel.

Packages

Package NPM Environment Description
static-tweets NPM Node.js Fetches tweet ASTs.
react-static-tweets NPM Browser + SSR React renderer for tweets given an AST.

Dynamic Client-Side Rendering

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.

Credit

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

License

MIT © Travis Fischer

Support my OSS work by following me on twitter twitter