react-tweet

repository·main·Indexed 23 days ago

https://github.com/vercel/react-tweet

A library for rendering tweets in React applications, compatible with Next.js, Vite, and React Server Components. It provides a default Twitter theme, the ability to create custom themes (such as custom-tweet-dub), and API utilities like getTweet, fetchTweet, enrichTweet, and the useTweet hook for client-side fetching.

Tokens
12.5K
Snippets
43
Records
72
Agent score
82%

What's inside react-tweet

  1. Implement Tweet loading using `useTweet` for Client Components

    main

    If your framework does not support React Server Components, use the useTweet hook within a 'use client' component.

    useTweet provides data, error, and isLoading states. You can use these to manage the rendering lifecycle: show the fallback during loading, and show a TweetNotFound component (or a custom one provided via components) if an error occurs or no data is returned. Once loaded, use the EmbeddedTweet component to render the tweet.

    'use client'
    
    import {
      type TweetProps,
      EmbeddedTweet,
      TweetNotFound,
      TweetSkeleton,
      useTweet,
    } from 'react-tweet'
    
    export const Tweet = ({
      id,
      apiUrl,
      fallback = <TweetSkeleton />,
      components,
      onError,
    }: TweetProps) => {
      const { data, error, isLoading } = useTweet(id, apiUrl)
    
      if (isLoading) return fallback
      if (error || !data) {
        const NotFound = components?.TweetNotFound || TweetNotFound
        return <NotFound error={onError ? onError(error) : error} />
      }
    
      return <EmbeddedTweet tweet={data} components={components} />
    }
  2. Enable caching for the Twitter API in Next.js App Router

    main

    To prevent server IP rate limiting from the Twitter API in production, use Next.js unstable_cache to cache the results of getTweet from react-tweet/api. This is typically implemented within a server component using Suspense and EmbeddedTweet.

    import { Suspense } from 'react'
    import { unstable_cache } from 'next/cache'
    import { TweetSkeleton, EmbeddedTweet, TweetNotFound } from 'react-tweet'
    import { getTweet as _getTweet } from 'react-tweet/api'
    
    const getTweet = unstable_cache(
      async (id: string) => _getTweet(id),
      ['tweet'],
      { revalidate: 3600 * 24 },
    )
    
    const TweetPage = async ({ id }: { id: string }) => {
      try {
        const tweet = await getTweet(id)
        return tweet ? <EmbeddedTweet tweet={tweet} /> : <TweetNotFound />
      } catch (error) {
        console.error(error)
        return <TweetNotFound error={error} />
      }
    }
    
    const Page = async ({ params }: { params: Promise<{ tweet: string }> }) => {
      const { tweet } = await params
      return (
        <Suspense fallback={<TweetSkeleton />}>
          <TweetPage id={tweet} />
        </Suspense>
      )
    }
    
    export default Page
  3. Enable cache for the Twitter API

    main

    To prevent server IPs from being rate limited by Twitter's syndication API, it is highly recommended to cache tweet data using a database like Redis or Vercel KV. This is especially important when using the SWR endpoint or React Server Components (RSC).

    import { Suspense } from 'react'
    import { TweetSkeleton, EmbeddedTweet, TweetNotFound } from 'react-tweet'
    import { fetchTweet, Tweet } from 'react-tweet/api'
    import { kv } from '@vercel/kv'
    
    async function getTweet(
      id: string,
      fetchOptions?: RequestInit
    ): Promise<Tweet | undefined> {
      try {
        const { data, tombstone, notFound } = await fetchTweet(id, fetchOptions)
    
        if (data) {
          await kv.set(`tweet:${id}`, data)
          return data
        } else if (tombstone || notFound) {
          // remove the tweet from the cache if it has been made private by the author (tombstone)
          // or if it no longer exists.
          await kv.del(`tweet:${id}`)
        }
      } catch (error) {
        console.error('fetching the tweet failed with:', error)
      }
    
      const cachedTweet = await kv.get<Tweet>(`tweet:${id}`)
      return cachedTweet ?? undefined
    }
    
    const TweetPage = async ({ id }: { id: string }) => {
      try {
        const tweet = await getTweet(id)
        return tweet ? <EmbeddedTweet tweet={tweet} /> : <TweetNotFound />
      } catch (error) {
        console.error(error)
        return <TweetNotFound error={error} />
      }
    }
    
    const Page = async ({ params }: { params: Promise<{ tweet: string }> }) => {
      const { tweet } = await params
      return (
        <Suspense fallback={<TweetSkeleton />}>
          <TweetPage id={tweet} />
        </Suspense>
      )
    }
    
    export default Page
  4. Configure next/image for react-tweet

    main

    To use next/image with react-tweet, you must first allow the Twitter image domains in your next.config.js under images.remotePatterns.

    Then, define a TwitterComponents object to map AvatarImg and MediaImg to the Next.js Image component, and pass this object to the Tweet component via the components prop.

    /** @type {import('next').NextConfig} */
    const nextConfig = {
      images: {
        remotePatterns: [
          { protocol: 'https', hostname: 'pbs.twimg.com' },
          { protocol: 'https', hostname: 'abs.twimg.com' },
        ],
      },
    }
    import Image from 'next/image'
    import type { TwitterComponents } from 'react-tweet'
    
    export const components: TwitterComponents = {
      AvatarImg: (props) => <Image {...props} />,
      MediaImg: (props) => <Image {...props} fill unoptimized />,
    }
    import { Tweet } from 'react-tweet'
    import { components } from './tweet-components'
    
    export default function Page() {
      return <Tweet id="2040511285998313827" components={components} />
    }
  5. Setup a custom API route for fetching tweets

    main

    To avoid IP rate limiting in production, it is highly recommended to host your own API route to fetch tweet data using getTweet from react-tweet/api. You can then point the Tweet component to this route using the apiUrl prop.

    Example implementation for a Vercel/Node.js API route:

    import type { VercelRequest, VercelResponse } from '@vercel/node'
    import { getTweet } from 'react-tweet/api'
    
    const handler = async (req: VercelRequest, res: VercelResponse) => {
      const tweetId = req.query.tweet
    
      if (req.method !== 'GET' || typeof tweetId !== 'string') {
        res.status(400).json({ error: 'Bad Request.' })
        return
      }
    
      try {
        const tweet = await getTweet(tweetId)
        res.status(tweet ? 200 : 404).json({ data: tweet ?? null })
      } catch (error) {
        console.error(error)
        res.status(400).json({ error: error.message ?? 'Bad request.' })
      }
    }
    
    export default handler

    Usage in component:

    <Tweet apiUrl={id && `/api/tweet/${id}`} id={id} />
  6. Best practices for publishing a custom theme

    main

    When creating and publishing a custom theme, follow these patterns to ensure compatibility with react-tweet consumers:

    1. Props: Use the props defined by the TweetProps type in your main Tweet component.
    2. Theming: Support CSS theme features (such as manual theme toggling). Use the base.css file from the Twitter theme as a reference for implementation.
    3. Environment Support: Provide support for both React Server Components (RSC) and client-side fetching (via SWR).
  7. Build a custom theme for react-tweet

    main

    If the default Twitter theme does not meet your requirements, you can build a custom theme using utility functions exported by react-tweet.

    To build a theme effectively, it is recommended to use the source code of the Twitter theme as a base. The core components of the react-tweet package include:

    • src/tweet.tsx: The async Tweet component designed for React Server Components (RSC) that fetches and renders tweet data.
    • src/twitter-theme/*.tsx: The individual components that constitute the Twitter theme.
    • src/swr.tsx: A Tweet component that uses SWR for client-side fetching, useful for environments where React Server Components are not supported.

    You can reference the custom-tweet-dub repository for a working example of a custom theme.

  8. Use react-tweet in a Vite project

    main

    To display a tweet in a Vite-based application, import the Tweet component from react-tweet and provide the tweet's unique ID via the id prop. The component handles the rendering of the tweet content automatically.

    import { Tweet } from 'react-tweet'
    
    export const IndexPage = () => <Tweet id="2040511285998313827" />
  9. Choose a tweet theme

    main

    By default, react-tweet uses the prefers-color-scheme CSS media feature to select a theme. You can manually control the theme by setting a data-theme attribute or a class on a parent element.

    // Using data-theme attribute
    <div data-theme="dark">
      <Tweet id="2040511285998313827" />
    </div>
    
    // Using class name
    <div className="dark">
      <Tweet id="2040511285998313827" />
    </div>