Introduction
react-bluesky-embed
allows you to embed post threads in your React application when using Next.js, Create React App, Vite, and more. This library does require using the Bluesky API. Post threads can be rendered statically, preventing the need to include an iframe and additional client-side JavaScript.
You can see how it in action in react-bluesky-embed-next.vercel.app/light/did:plc:gru662w3omynujkgwebgeeof/3lbirib5xnc2u. Replace the postThread ID in the URL to see other post threads.
This library is fully compatible with React Server Components.
Installation
Install react-bluesky-embed
using your package manager of choice:
npm install react-bluesky-embed
Now follow the usage instructions for your framework or builder:
Important: Before going to production, we recommend enabling cache for the Bluesky API as server IPs might get rate limited by Bluesky.
Choosing a theme
Toggling theme manually
The closest theme
prop can determine the theme of the postThread. You can set it to light
or dark
, like so:
<div className="max-w-[672px]">
<PostThread
params={{
did: "did:plc:gru662w3omynujkgwebgeeof",
rkey: "3lbirib5xnc2u",
}}
theme="dark"
// set the depth to 1+ to show replies
config={{
depth: 6,
}}
// only show the replies
hidePost={false}
/>
</div>
Enabling cache for the Bluesky API
Rendering post threads requires making a call to Bluesky’s API. Getting rate limited by that API is very hard but it’s possible if you’re relying only on the endpoint we provide for SWR (react-bluesky-embed.vercel.app/api/postThread/:did-:rkey
) as the IPs of the server are making many requests to the syndication API. This also applies to RSC where the API endpoint is not required but the server is still making the request from the same IP.
To prevent this, you can use a db like Redis or Vercel KV to cache the post threads. For example using Vercel KV:
import { Suspense } from "react";
import {
PostThreadSkeleton,
EmbeddedPostThread,
PostThreadNotFound,
} from "react-bluesky-embed";
import { fetchPostThread, PostThread } from "react-bluesky-embed/api";
import { kv } from "@vercel/kv";
async function getPostThread(
params: PostThreadParams,
config?: PostThreadConfig
): Promise<PostThread | undefined> {
try {
const { data, tombstone, notFound } = await fetchPostThread(
params,
config
);
if (data) {
await kv.set(`postThread:${params}`, data);
return data;
} else if (tombstone || notFound) {
// remove the postThread from the cache if it has been made private by the author (tombstone)
// or if it no longer exists.
await kv.del(`postThread:${params}`);
}
} catch (error) {
console.error("fetching the postThread failed with:", error);
}
const cachedPostThread = await kv.get<PostThread>(`postThread:${params}`);
return cachedPostThread ?? undefined;
}
const PostThreadPage = async ({
params,
}: {
params: { PostThreadParams };
}) => {
try {
const postThread = await getPostThread(params);
return postThread ? (
<EmbeddedPostThread postThread={postThread} />
) : (
<PostThreadNotFound />
);
} catch (error) {
console.error(error);
return <PostThreadNotFound error={error} />;
}
};
const Page = ({
params,
}: {
params: { postThread: PostThreadParams };
}) => (
<Suspense fallback={<PostThreadSkeleton />}>
<PostThreadPage params={params.postThread} />
</Suspense>
);
export default Page;
You can see it working at react-bluesky-embed-next.vercel.app/light/vercel-kv/did:plc:gru662w3omynujkgwebgeeof/3lbirib5xnc2u (source).
If you’re using Next.js then using unstable_cache
works too.