PreviewCode Installationnpm install react-tweetCopyInstallation React Server Component (Next.js 13+):Copy and paste the following code into your project./* eslint-disable @next/next/no-img-element */ import { Suspense } from "react"; import { enrichTweet, type EnrichedTweet, type TweetProps, type TwitterComponents, } from "react-tweet"; import { getTweet, type Tweet } from "react-tweet/api"; import { cn } from "@/lib/utils"; interface TwitterIconProps { className?: string; [key: string]: unknown; } const Twitter = ({ className, ...props }: TwitterIconProps) => ( <svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" className={className} {...props} > <g> <path fill="none" d="M0 0h24v24H0z"></path> <path d="M22.162 5.656a8.384 8.384 0 0 1-2.402.658A4.196 4.196 0 0 0 21.6 4c-.82.488-1.719.83-2.656 1.015a4.182 4.182 0 0 0-7.126 3.814 11.874 11.874 0 0 1-8.62-4.37 4.168 4.168 0 0 0-.566 2.103c0 1.45.738 2.731 1.86 3.481a4.168 4.168 0 0 1-1.894-.523v.052a4.185 4.185 0 0 0 3.355 4.101 4.21 4.21 0 0 1-1.89.072A4.185 4.185 0 0 0 7.97 16.65a8.394 8.394 0 0 1-6.191 1.732 11.83 11.83 0 0 0 6.41 1.88c7.693 0 11.9-6.373 11.9-11.9 0-.18-.005-.362-.013-.54a8.496 8.496 0 0 0 2.087-2.165z"></path> </g> </svg> ); const Verified = ({ className, ...props }: TwitterIconProps) => ( <svg aria-label="Verified Account" viewBox="0 0 24 24" className={className} {...props} > <g fill="currentColor"> <path d="M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z" /> </g> </svg> ); export const truncate = (str: string | null, length: number) => { if (!str || str.length <= length) return str; return `${str.slice(0, length - 3)}...`; }; const Skeleton = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => { return ( <div className={cn("rounded-md bg-primary/10", className)} {...props} /> ); }; export const TweetSkeleton = ({ className, ...props }: { className?: string; [key: string]: unknown; }) => ( <div className={cn( "flex size-full max-h-max min-w-72 flex-col gap-2 rounded-lg border p-4", className, )} {...props} > <div className="flex flex-row gap-2"> <Skeleton className="size-10 shrink-0 rounded-full" /> <Skeleton className="h-10 w-full" /> </div> <Skeleton className="h-20 w-full" /> </div> ); export const TweetNotFound = ({ className, ...props }: { className?: string; [key: string]: unknown; }) => ( <div className={cn( "flex size-full flex-col items-center justify-center gap-2 rounded-lg border p-4", className, )} {...props} > <h3>Tweet not found</h3> </div> ); export const TweetHeader = ({ tweet }: { tweet: EnrichedTweet }) => ( <div className="flex flex-row justify-between tracking-tight"> <div className="flex items-center space-x-2"> <a href={tweet.user.url} target="_blank" rel="noreferrer"> <img title={`Profile picture of ${tweet.user.name}`} alt={tweet.user.screen_name} height={48} width={48} src={tweet.user.profile_image_url_https} className="overflow-hidden rounded-full border border-transparent" /> </a> <div> <a href={tweet.user.url} target="_blank" rel="noreferrer" className="flex items-center whitespace-nowrap font-semibold" > {truncate(tweet.user.name, 20)} {tweet.user.verified || (tweet.user.is_blue_verified && ( <Verified className="ml-1 inline size-4 text-blue-500" /> ))} </a> <div className="flex items-center space-x-1"> <a href={tweet.user.url} target="_blank" rel="noreferrer" className="text-sm text-gray-500 transition-all duration-75" > @{truncate(tweet.user.screen_name, 16)} </a> </div> </div> </div> <a href={tweet.url} target="_blank" rel="noreferrer"> <span className="sr-only">Link to tweet</span> <Twitter className="size-5 items-start text-[#3BA9EE] transition-all ease-in-out hover:scale-105" /> </a> </div> ); export const TweetBody = ({ tweet }: { tweet: EnrichedTweet }) => ( <div className="break-words leading-normal tracking-tighter"> {tweet.entities.map((entity, idx) => { switch (entity.type) { case "url": case "symbol": case "hashtag": case "mention": return ( <a key={idx} href={entity.href} target="_blank" rel="noopener noreferrer" className="text-sm font-normal text-gray-500" > <span>{entity.text}</span> </a> ); case "text": return ( <span key={idx} className="text-sm font-normal" dangerouslySetInnerHTML={{ __html: entity.text }} /> ); } })} </div> ); export const TweetMedia = ({ tweet }: { tweet: EnrichedTweet }) => ( <div className="flex flex-1 items-center justify-center"> {tweet.video && ( <video poster={tweet.video.poster} autoPlay loop muted playsInline className="rounded-xl border shadow-sm" > <source src={tweet.video.variants[0].src} type="video/mp4" /> Your browser does not support the video tag. </video> )} {tweet.photos && ( <div className="relative flex transform-gpu snap-x snap-mandatory gap-4 overflow-x-auto"> <div className="shrink-0 snap-center sm:w-2" /> {tweet.photos.map((photo) => ( <img key={photo.url} src={photo.url} title={"Photo by " + tweet.user.name} alt={tweet.text} className="h-64 w-5/6 shrink-0 snap-center snap-always rounded-xl border object-cover shadow-sm" /> ))} <div className="shrink-0 snap-center sm:w-2" /> </div> )} {!tweet.video && !tweet.photos && // @ts-ignore tweet?.card?.binding_values?.thumbnail_image_large?.image_value.url && ( <img // @ts-ignore src={tweet.card.binding_values.thumbnail_image_large.image_value.url} className="h-64 rounded-xl border object-cover shadow-sm" alt={tweet.text} /> )} </div> ); export const MagicTweet = ({ tweet, components, className, ...props }: { tweet: Tweet; components?: TwitterComponents; className?: string; }) => { const enrichedTweet = enrichTweet(tweet); return ( <div className={cn( "relative flex size-full max-w-lg flex-col gap-2 overflow-hidden rounded-lg border p-4 backdrop-blur-md", className, )} {...props} > <TweetHeader tweet={enrichedTweet} /> <TweetBody tweet={enrichedTweet} /> <TweetMedia tweet={enrichedTweet} /> </div> ); }; /** * TweetCard (Server Side Only) */ export const TweetCard = async ({ id, components, fallback = <TweetSkeleton />, onError, ...props }: TweetProps & { className?: string; }) => { const tweet = id ? await getTweet(id).catch((err) => { if (onError) { onError(err); } else { console.error(err); } }) : undefined; if (!tweet) { const NotFound = components?.TweetNotFound || TweetNotFound; return <NotFound {...props} />; } return ( <Suspense fallback={fallback}> <MagicTweet tweet={tweet} {...props} /> </Suspense> ); }; export TweetCard;CopyExpandInstallation Client SideCopy and paste the following code into your project."use client"; import { TweetProps, useTweet } from "react-tweet"; import { MagicTweet, TweetNotFound, TweetSkeleton, } from "@/components/magicui/tweet-card"; const ClientTweetCard = ({ id, apiUrl, fallback = <TweetSkeleton />, components, fetchOptions, onError, ...props }: TweetProps & { className?: string }) => { const { data, error, isLoading } = useTweet(id, apiUrl, fetchOptions); if (isLoading) return fallback; if (error || !data) { const NotFound = components?.TweetNotFound || TweetNotFound; return <NotFound error={onError ? onError(error) : error} />; } return <MagicTweet tweet={data} components={components} {...props} />; }; export ClientTweetCard;CopyExpand Usage To render on server side using RSC (Next.js 13): import { TweetCard } from "@/components/magicui/tweet-card.tsx"; export default async function App() { return <TweetCard id="1441032681968212480" />; } To render on client side: "use client"; import { ClientTweetCard } from "@/components/magicui/client-tweet-card.tsx"; export default function App() { return <ClientTweetCard id="1441032681968212480" />; } Examples Tweet Card With Image Carousel PreviewCode Tweet Card With Meta URL Preview PreviewCode Props ClientTweetCard PropTypeDefaultDescriptionidstring-The id of the tweet to display. TweetCard PropTypeDefaultDescriptionidstring-The id of the tweet to display. Credits This component is built on top of React Tweet.