pluginIsland

ページの一部をReact App化するプラグイン。AstroのIslandを再現しています。

Table of Contents

How To Use

動作させるには以下パッケージのいずれが必要です。

  • @vitejs/plugin-react
  • @vitejs/plugin-react-swc

これによってIslandのJavaScriptにReactのimportが自動挿入され、同時に開発モードでIsland内のHMRが効くようになります。

$ npm install --save-dev @vitejs/plugin-react
./minista.config.js
import { pluginIsland } from "minista"
import react from "@vitejs/plugin-react"

export default {
  plugins: [pluginIsland(), react()],
}
./src/pages/index.jsx
import { Counter } from "../components/counter"

export default function () {
  return <Counter client:load />
}

Options

Default
pluginIsland({
  useSplitPages: true,
  outName: "island-[index]",
  rootAttrName: "island",
  rootDOMElement: "div",
  rootStyle: { display: "contents" },
})

useSplitPages

  • 型: boolean
  • デフォルト: true

ページ毎に必要なJavaScriptを分割して読み込みます。

outName

  • 型: string
  • デフォルト: "island-[index]"

出力ファイル名。拡張子は含みません。以下の動的出力タグを使用できます。

  • [index]:生成された順番(開始: 1)

rootAttrName

  • 型: string
  • デフォルト: "island"

ハイドレーションを行うルート要素のデータ属性名に使われる名前。

  • 例: data-island-client-directive

rootDOMElement

  • 型: "div" | "span"
  • デフォルト: "div"

ハイドレーションを行うルート要素のHTMLタグ。

rootStyle

  • 型: React.CSSProperties
  • デフォルト: { display: "contents" }

ハイドレーションを行うルート要素に付与するスタイル。

Client Island

ministaはReactのJSXテンプレートを 静的なHTMLに変換し、クライアントサイドのJavaScriptを取り除きます。

import { Counter } from "../components/counter"

export default function () {
  return <Counter />
}

しかし、UIコンポーネントをインタラクティブにしたいシーンもあります。その場合は client:* ディレクティブを付与してください。ministaはその部分だけのJavaScriptを生成してハイドレーションします。client:* ディレクティブの種類と動作は次項を参照ください。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:load />
}

※ハイドレーションを行うコンポーネン配下で読み込んだ画像パスはルートパス・または絶対パスとなり、埋め込み用の相対パスは反映されません

Client Directive

静的なHTMLに変換されるJSXの一部をReact Appとして動作させる効果があります。client:* ディレクティブ配下ではReactの useState useEffect などインタラクティブな実装がすべて使えます。client:* ディレクティブはReact ComponentsまたはHTMLタグに付与でき、同時にpropsを渡すことも可能です。

client:load

ページの読み込みと同時に、コンポーネントをハイドレーションします。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:load />
}

client:idle

ページの初期読み込みが終わり、ブラウザが待機状態になったときにコンポーネントをハイドレーションします。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:idle />
}
  • timeout:初期読み込みがこの時間に到達した場合にハイドレーションを優先して実行
    • 実装: requestIdleCallbacktimeout
import { Counter } from "../components/counter"

export default function () {
  return <Counter client:idle={{ timeout: 5000 }} />
}

client:visible

コンポーネントが画面内に表示されたときにハイドレーションします。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:visible />
}
  • rootMargin:コンポーネントの認識範囲を拡大
import { Counter } from "../components/counter"

export default function () {
  return <Counter client:visible={{ rootMargin: "200px" }} />
}

client:media

CSSメディアクエリの条件が満たされたときに、コンポーネントをハイドレーションします。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:media="(max-width: 500px)" />
}

client:only

静的HTMLを出力せずに、ReactDOM.createRoot でコンポーネントをレンダリングします。タイミングは client:load と同じくページの読み込みと同時。静的なHTMLを初期表示させたくない場合に有効ですが、レンダリング前の高さを確保できないためCumulative Layout Shift(CLS)による画面のガタつきに注意が必要です。

import { Counter } from "../components/counter"

export default function () {
  return <Counter client:only />
}
  • slot="fallback":直下要素に付与することでレンダリング前に表示
import { Counter } from "../components/counter"

export default function () {
  return (
    <div client:only>
      <div slot="fallback">Loading...</div>
      <Counter />
    </div>
  )
}