Skip to Content
API Reference

API Reference

This is the reference for the utility functions that react-x-embed provides for building your own tweet components or simply fetching a tweet. Navigate to the docs for the Twitter theme if you want to render the existing Tweet components instead.

Fetching

getTweet

import { getTweet, type Tweet } from 'react-x-embed/api' function getTweet( id: string, fetchOptions?: RequestInit, ): Promise<Tweet | undefined>

Fetches and returns a Tweet. It accepts the following params:

  • id - string: the tweet ID. For example in https://x.com/reactiive_/status/2040511285998313827 the tweet ID is 2040511285998313827.
  • fetchOptions - RequestInit (Optional): options to pass to fetch.

If a tweet is not found it returns undefined.

fetchTweet

function fetchTweet( id: string, fetchOptions?: RequestInit, ): Promise<{ data?: Tweet | undefined tombstone?: true | undefined notFound?: true | undefined }>

Fetches and returns a Tweet just like getTweet, but it also returns additional information about the tweet:

  • data - Tweet (Optional): The tweet data.
  • tombstone - true (Optional): Indicates if the tweet has been made private.
  • notFound - true (Optional): Indicates if the tweet was not found, or the response could not be rendered. See isValidTweet.

isValidTweet

import { isValidTweet } from 'react-x-embed/api' const isValidTweet: (tweet: unknown) => tweet is Tweet

Returns whether a syndication response can actually be rendered.

The syndication API is undocumented and changes without notice. Historically each change surfaced as a crash deep inside rendering — Cannot read properties of undefined (reading 'screen_name') — because the payload was trusted and dereferenced directly. A deleted or suspended account is enough to trigger it: X returns a tweet-shaped object with no user at all.

This checks only the fields the renderer dereferences without guarding, so an unexpected payload degrades to “not found” instead of taking down the page. It is deliberately not a full schema check — unknown or added fields are fine, and tweets with no entities stay valid.

Both fetchTweet and useTweet apply it already. You only need it directly if you fetch the syndication API yourself:

const tweet = await fetchFromSomewhereElse(id) if (!isValidTweet(tweet)) return <TweetNotFound />

getOEmbed

import { getOEmbed } from 'react-x-embed/api' function getOEmbed(url: string): Promise<any>

Fetches X’s oEmbed  response for a post URL, which is a blob of blockquote HTML plus a <script> tag pointing at platform.twitter.com. Returns undefined on a 404 and throws on any other non-OK status.

This is not how this library renders posts, and it is not what you want in most cases. Rendering its html reintroduces exactly what react-x-embed exists to avoid: a third-party script, X’s own styles, and a layout you can’t theme or server-render. Use getTweet instead.

It remains exported for the narrow case of needing X’s canonical oEmbed metadata — an author attribution string, or the URL X considers canonical — and is inherited from upstream unchanged. The response is typed any because the endpoint is undocumented; it takes no fetchOptions, so it can’t participate in your framework’s cache the way getTweet does.

useTweet

If your app supports React Server Components, use getTweet instead.

import { useTweet } from 'react-x-embed' const useTweet: ( id?: string, apiUrl?: string, fetchOptions?: RequestInit, ) => { isLoading: boolean data: Tweet | null | undefined error: any }

SWR hook for fetching a tweet in the browser. It accepts the following parameters:

  • id - string: the tweet ID. For example in https://x.com/reactiive_/status/2040511285998313827 the tweet ID is 2040511285998313827. This parameter is not used if apiUrl is provided.
  • apiUrl - string: the API URL to fetch the tweet from. Defaults to https://react-tweet.vercel.app/api/tweet/:id.
  • fetchOptions - RequestInit (Optional): options to pass to fetch. Try to pass down a reference to the same object to avoid unnecessary re-renders.

Note: the default endpoint is upstream’s public deployment. It is a shared, rate-limited service that this project does not operate — host your own route before going to production.

If neither id nor apiUrl is provided, isLoading is false: SWR has no key, so no request is made and there is nothing to wait for. This matters when the id comes from a route that hasn’t resolved yet — reporting isLoading there would strand the component on a skeleton forever.

We highly recommend adding your own API endpoint in apiUrl for production:

const tweet = useTweet(null, id && `/api/tweet/${id}`)

It’s likely you’ll never use this hook directly, and apiUrl is passed as a prop to a component instead:

<Tweet apiUrl={id && `/api/tweet/${id}`} />

Or if the tweet component already knows about the endpoint it needs to use, you can use id instead:

<Tweet id={id} />

useMounted

import { useMounted } from 'react-x-embed' const useMounted: () => boolean

Returns false on the server and on the first client render, then true once mounted. Used internally to defer rendering that would otherwise mismatch between server and client. Exported for custom themes that need the same guard.

Deriving

enrichTweet

import { enrichTweet, type EnrichedTweet } from 'react-x-embed' const enrichTweet: (tweet: Tweet) => EnrichedTweet

Enriches a Tweet as returned by getTweet with additional data. This is useful to more easily build custom tweet components.

It returns an EnrichedTweet, which resolves every URL the UI needs (url, like_url, reply_url, user.follow_url), splits the tweet text into typed entities, and flattens any link preview into card.

enrichCard

import { enrichCard, type EnrichedCard } from 'react-x-embed' const enrichCard: (card?: TweetCard) => EnrichedCard | undefined

Flattens a tweet’s link preview into render-ready fields: url, domain, title, description, image, and large.

X models cards as an untyped binding_values bag whose keys vary by card type, and ships up to seven renditions of the preview image. This picks a rendition sized for an embed rather than the 1600×900 original, and returns undefined when there’s nothing worth rendering — cards with no title (player, unified_card, ad formats), since the link already appears inline in the tweet text.

Already applied by enrichTweet; you only need it directly when working with a raw Tweet.

Media

These are useful when building custom components that render media themselves.

getMediaUrl

const getMediaUrl: ( media: MediaDetails, size: 'small' | 'medium' | 'large', ) => string

Returns the URL for a given rendition of a photo or video thumbnail.

getMediaSrcSet

const getMediaSrcSet: (media: MediaDetails) => string | undefined

Builds a srcset from the renditions X advertises, so the browser can pick based on device pixel ratio. Requesting only the 680px rendition — as upstream did — is soft on retina, where the embed renders at up to 550 CSS pixels.

Returns undefined when X reports fewer than two distinct widths, since there would be nothing to choose between.

getMediaBackgroundColor

const getMediaBackgroundColor: ( media: MediaDetails, photos?: TweetPhoto[], ) => string | undefined

Returns the dominant colour X computed for an image, as a CSS rgb() string, for use as a placeholder while it loads.

Pass tweet.photos as the second argument: the syndication API reports this colour on that parallel array rather than on mediaDetails, matched by URL. Videos have no entry there, so this returns undefined for them.

isMediaAvailable

const isMediaAvailable: (media: MediaDetails) => boolean

Whether X still serves this media. Media withheld after publication — DMCA takedowns, region blocks — remains in the payload but 404s on fetch, rendering as a broken image. Filter with this before rendering.

getMp4Video and getMp4Videos

type VideoQuality = 'low' | 'medium' | 'high' const getMp4Videos: (media: MediaAnimatedGif | MediaVideo) => VideoVariant[] const getMp4Video: ( media: MediaAnimatedGif | MediaVideo, quality?: VideoQuality, ) => VideoVariant | undefined

getMp4Videos returns every mp4 rendition sorted by descending bitrate. getMp4Video picks one — medium by default, which skips the highest bitrate since it is usually far larger than an inline embed needs.

Some videos are served with an HLS-only variant list. Rather than returning undefined — which crashed the player, since callers dereference .urlgetMp4Video falls back to the HLS rendition.

getHlsVideo

const getHlsVideo: ( media: MediaAnimatedGif | MediaVideo, ) => VideoVariant | undefined

Returns the HLS (application/x-mpegURL) rendition when X provides one.

Safari supports HLS natively and plays it far more reliably than X’s mp4 renditions, which don’t consistently honour byte-range requests. Render it as an additional <source> before the mp4 so Safari prefers it and other browsers fall through.

normalizeAvatarUrl

const normalizeAvatarUrl: (src: string) => string

Rewrites a profile image URL to a higher-resolution variant.

The syndication API only ever reports the _normal rendition, which is 48×48 — blurry on retina, since the avatar renders at 48 CSS pixels. This swaps it for _400x400, the same asset at 400px. It also avoids a class of broken avatars, as X purges _normal renditions of older assets more aggressively.

This is a mitigation, not a cure: if the underlying asset is gone, every rendition 404s. URLs that don’t match the expected shape are returned unchanged.

Last updated on