[小ネタ]Astroで動的にしたいコンポーネントやライブラリにはclientディレクティブを付与しましょう

Astroで「あれ?JSが動かない」「ボタンを押しても何も起きない」などと慌てそうになったら、Clientディレクティブを思い出してみてください。私はreact-shareをつかう時にすっかり忘れていました。
2024.01.06

こんにちは。AWS事業本部モダンアプリケーションコンサルティング部に所属している今泉です。

みなさんAstroはご存知ですか?

Astroはウェブサイト作成が得意なフレームワークであり、ブログサイトなどのコンテンツ駆動のサイトを作成できます。

以前同僚がサクッとWebサイトを作ってみたブログに特徴が記載されているため、ご参照ください。

私もAstroを利用して個人的なブログを作成しており、数ヶ月運用している中で、ちょっとハマりそうな小ネタがいくつか出てきたので紹介します。

いきなりまとめ

  • Astroでは意識をしなければ静的なHTMLを生成してくれる
    • ReactやVueなどで作成したコンポーネントをHTMLとCSSのみへと自動的にレンダリングする
  • そのため、動的にJSで挙動を制御する必要があるようなコンポーネントやライブラリを利用する際は client:* ディレクティブを利用する

Astroアイランドという特徴

AstroではAstroアイランドというアーキテクチャを採用しています。

islands-architecture-image

Islands Architecture: Jason Millerより引用

各アイランドは独立しており、一つのページ上で React,Vue,Svelteなど様々なフレームワークを利用することも可能です。(びっくりですよね)

そのアイランドを作成する際に、Astroではパフォーマンス低下などを防ぐため、デフォルトで静的な状態でレンダリングします。

デフォルトでは、AstroはすべてのUIコンポーネントをHTMLとCSSのみへと自動的にレンダリングし、クライアントサイドのJavaScriptを自動的に取り除きます。

そのため、動的に状態を持つコンポーネントやライブラリを利用したい際は、意識して動的なコンポーネントを配置する必要があります。

具体例: react-shareを使ったSNSシェアボタンで起こったこと

例えば以下は、react-share というライブラリを利用してSNSシェアボタンを配置している画像とコードになります。

20240106_astro_hydration_disable_share_button

// コンポーネント
export const SocialShareButtons: React.FC<SocialShareArgs> = (props) => {
  const { url, title } = props
  return (
    <>
      <HatenaShareButton url={url} className="mx-2 flex-col">
        <HatenaIcon round size={40} />
        <HatenaShareCount url={url} className="text-blue-600 font-semibold" />
      </HatenaShareButton>
      <FacebookShareButton url={url} className="mx-2 flex-col">
        <FacebookIcon round size={40} />
        <FacebookShareCount url={url} className="text-blue-600 font-semibold" />
      </FacebookShareButton>
      <TwitterShareButton url={url} title={title} className="mx-2">
        <XIcon size={40} round />
      </TwitterShareButton>
    </>
  )
}
// .astroファイル
const titleUrl = `${BLOG_URL}${slug}`
const urlPath = new UrlPath(titleUrl, image.url)
---
<Layout title={title}>
    <div class="m-auto">
        // ↑省略
        <div class="flex items-start my-4">
          // ↓ここで呼び出している
          <SocialShareButtons
              title={title}
              url={titleUrl}
          />
        </div>
      </div>
    <div>
</Layout>

しかし、こちらは「JSを排除した静的なHTMLとCSSが生成されていることを忘れている」ため、SNSアイコンを何回クリックしようと何も起きません。

私の解決方法

以下のようにたった一行加えただけです。

// .astroファイル
---
<Layout title={title}>
    <div class="m-auto">
        <div class="flex items-start my-4">
          <SocialShareButtons
              // ↓この1行を加える
              client:load
              title={title}
              url={titleUrl}
          />
        </div>
      </div>
    <div>
</Layout>

上のclient:loadの部分はClient Directivesという指示の一つで、ページがロードされる際に即座にコンポーネントJSを直ちにハイドレートしてくれます。

client:loadなどは静的な部分はHTMLとしてサーバーでレンダリングして生成してくれていますが、HTMLのサーバーレンダリングを一切しないclient:onlyなどのディレクティブもあります。

Reactを利用したCSR|Astro.js 簡易チュートリアルで利用しているように、クライアントサイドで動的な制御が必要な場合などに利用できるようです。

さいごに

今回のClient Directivesは公式ドキュメントにわりとでかでかと書いてくれている概念ですが、Reactを使っている感覚でたまに忘れてしまいそうになるときがあります。

皆さんもAstroを利用していて「あれ?なんで動かないんだ?」などと慌てそうになったときには、思い出してみてください。

最後まで読んでいただいてありがとうございました。