コンテンツにスキップ

Next.jsでYoutubeやTwitterのリンクを埋め込む

リンクだけあっても味気ないので、埋め込み対応をしてみました。

前提

  • remark, remark-rehype, rehype-stringify がインストール済みであること
  • npm i --save-dev remark remark-rehype rehype-stringify

対応方法

@remark-embedder/transformer-oembedを使って変換します。

設定方法

@remark-embedder/transformer-oembedをインストールします。

npm i --save-dev @remark-embedder/transformer-oembed

コンポーネント markdownToHtml.js が以下のようなものだとします:

```ts:markdownToHtml.js import { remark } from "remark"; import remarkRehype from "remark-rehype"; import rehypeStringify from "rehype-stringify"; import rehypeExternalLinks from "rehype-external-links"; import remarkEmbedder from "@remark-embedder/core"; import type { Config } from "@remark-embedder/transformer-oembed"; import oembedTransformer from "@remark-embedder/transformer-oembed";

export const markdownToHtml = async (markdown: string) => { const result = await remark() .use(remarkEmbedder, { transformers: [ [ oembedTransformer, { params: { maxwidth: 550, omit_script: true, lang: "ja", dnt: true, }, } as Config, ], ], }) .use(remarkRehype, { allowDangerousHtml: true }) .use(rehypeStringify) .process(markdown); return result.toString(); };

markdownToHtml.js を使ってページを表示します。

```ts:index.tsx
import { InferGetStaticPropsType, NextPage } from "next";
import { markdownToHtml } from "../../lib/markdownToHtml";

type Props = InferGetStaticPropsType<typeof getStaticProps>;

const Home: NextPage<Props> = ({ html }) => {
  return (
    <div
      dangerouslySetInnerHTML={{
        __html: html,
      }}
    ></div>
  );
};

export const getStaticProps = async () => {
  return {
    props: {
      html: await markdownToHtml("https://www.youtube.com/watch?v=dQw4w9WgXcQ"),
    },
  };
};

export default Home;

出力結果

YouTube の場合

<iframe
  width="356"
  height="200"
  src="https://www.youtube.com/embed/dQw4w9WgXcQ?feature=oembed"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
  allowfullscreen
  title="Rick Astley - Never Gonna Give You Up (Official Music Video)"
></iframe>

Twitter の場合

<blockquote
  class="twitter-tweet"
  data-width="550"
  data-lang="ja"
  data-dnt="true"
>
  <p lang="en" dir="ltr">
    Creators can now sign up and earn a living directly on Twitter in the EU,
    UK, and EEA.<br /><br />Tap on “Monetization” in settings to apply today.<br /><br />For
    a full list of available countries see our Help Center:
    <a href="https://t.co/YbBw0EVKqJ">https://t.co/YbBw0EVKqJ</a>
  </p>
  — Twitter (@Twitter)
  <a
    href="https://twitter.com/Twitter/status/1649507477325488131?ref_src=twsrc%5Etfw"
    >2023年4月21日</a
  >
</blockquote>

ツイートを以下のような埋め込みにする場合は、widgets.js を使って<iframe>に変換します。

https://twitter.com/Twitter/status/1649507477325488131

```ts:index.tsx import { useEffect } from "react"; import { InferGetStaticPropsType, NextPage } from "next"; import { markdownToHtml } from "../../lib/markdownToHtml";

type Props = InferGetStaticPropsType;

const Home: NextPage = ({ html }) => { useTweetEmbed(); return (

); };

export const getStaticProps = async () => { return { props: { html: await markdownToHtml( "https://twitter.com/Twitter/status/1649507477325488131" ), }, }; };

export const useTweetEmbed = () => { useEffect(() => { const script = document.createElement("script"); script.src = "https://platform.twitter.com/widgets.js"; document.body.appendChild(script);

return () => {
  document.body.removeChild(script);
};

}, []); };

export default Home; ```