Bluesky post embeds in Next.js

@samuel.bsky.team

The way that Bluesky post embeds work doesn't mesh very nicely with React since it swaps out elements in the DOM that React is trying to keep track of, and I was having a lot of trouble getting it work with Next.js due to its fancy router. Luckily, the embed script is very simple, and you can simply reimplement it as a React component. The snippet doesn't do a whole lot—it swaps out the placeholder blockquote for an iframe, then listens to messages that the iframe sends up to the parent frame to set its height. This is because iframe content can't influence the size of the frame, so we have to set it manually.

With this component, all you need is the post URI - you can get this from the embed snippet we give you at embed.bsky.app in the data-bluesky-uri attribute.

This uses Next.js and TailwindCSS, but it should be pretty obvious how to adapt it for your specific project.

"use client";

import { useEffect, useId, useState } from "react";
import { usePathname } from "next/navigation";

const WEBSITE_URL = "https://example.com" // your website goes here
const EMBED_URL = "https://embed.bsky.app";

export function BlueskyPostEmbed({ uri }: { uri: string }) {
  const id = useId();
  const pathname = usePathname();
  const [height, setHeight] = useState(0);

  useEffect(() => {
    const abortController = new AbortController();
    const { signal } = abortController;
    window.addEventListener(
      "message",
      (event) => {
        if (event.origin !== EMBED_URL) {
          return;
        }

        const iframeId = (event.data as { id: string }).id;
        if (id !== iframeId) {
          return;
        }

        const internalHeight = (event.data as { height: number }).height;
        if (internalHeight && typeof internalHeight === "number") {
          setHeight(internalHeight);
        }
      },
      { signal },
    );

    return () => {
      abortController.abort();
    };
  }, [id]);

  const ref_url = WEBSITE_URL + pathname;

  const searchParams = new URLSearchParams();
  searchParams.set("id", id);
  searchParams.set("ref_url", encodeURIComponent(ref_url));

  const src = `${EMBED_URL}/embed/${uri.slice("at://".length)}?${searchParams.toString()}`

  return (
    <div
      className="flex max-w-[600px] w-full bluesky-embed"
      data-uri={uri}
    >
      <iframe
        className="w-full block border-none flex-grow"
        style={{ height }}
        data-bluesky-uri={uri}
        src={src}
        width="100%"
        frameBorder="0"
        scrolling="no"
      />
    </div>
  );
}

Here it is in action:

to write a blog post one must first invent a blogging protocol

— Samuel (@samuel.bsky.team) October 9, 2024 at 11:00 PM

Hope that helps somebody!

samuel.bsky.team
Samuel

@samuel.bsky.team

https://mozzius.dev
dev at bluesky. react native is good actually
📍London

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)