12k
All articles

ThreeUI入門

ReactでThreeUIを導入し、アニメーション付き3D背景を追加する方法を解説。アセットのパス修正や遅延読み込み、動きを抑える設定も紹介します。

OpenReplay Team
OpenReplay Team
ThreeUI入門

ThreeUIは、Meng To氏が開発したReactコンポーネントカタログです。threeui.comで公開されており、npmでは@designcodeio/threeuiとして配布されています。各コンポーネントはそれぞれ独自のアニメーションレンダラー(コンポーネントによってThree.js、生のWebGL、またはCanvas 2D)を内蔵しています。そのため、シーン、カメラ、マテリアルを自前で構築しなくても、コンポーネントをインポートするだけでアニメーション付きの3Dヒーロー背景を追加できます。

ユーザーのスクロールを止めるようなヒーローセクションを作りたくても、Three.jsのチュートリアルは画面に何かが表示されるまでに、シーングラフ、ライティング構成、レンダーループの説明から始まります。本ガイドでは、ThreeUIのインストール、ヒーローの背後に背景コンポーネントを1つレンダリングする方法、アセットパスの問題の解決方法、そしてページにとって追加の負荷に見合う価値があるかどうかの判断基準を解説します。

重要なポイント

  • npm install @designcodeio/threeuiでThreeUIをインストールし、アプリのエントリーポイントで@designcodeio/threeui/style.cssを一度だけインポートします。
  • ThreeUIはreactとreact-dom(18または19)、およびthreeをpeer dependenciesとして指定しています。
  • 完全なHTMLドキュメントをレンダリングするコンポーネントは、ルート相対URLからランタイムファイルを読み込みます。そのため、node_modules/@designcodeio/threeui/lib-dist/assets/からファイルをコピーするか、sourceUrlまたはassetBaseUrlを設定してください。
  • コンポーネントが公開しているバリアントやコントロールの範囲を超えるカスタマイズには、ソースコードの編集が必要です。
  • ThreeUIコンポーネントは遅延読み込みし、prefers-reduced-motionが設定されている場合はスキップし、3D効果が価値を生まない場面では静止画像を使いましょう。

ThreeUIとは? 誰に向いているのか

ThreeUIは、ヒーロー、背景、インタラクティブ要素など、すぐに使えるアニメーションセクションを提供し、通常のReactページにそのまま組み込めるようにします。シーングラフの仕組みを理解している必要はありません。コンポーネントを選んでレンダリングし、propsを設定するだけです。

three-uiという名前のプロジェクトは他にもいくつか存在します。たとえばpoki-archive/three-uiやnirholas/three-uiなどですが、これらは無関係です。本ガイドで扱うのは、threeui.comおよびMengTo/threeuiリポジトリで公開されているMeng To氏のThreeUIです。

ThreeUIにはCommunityとProの2つのティアがあります。CommunityコンポーネントはMITライセンスで、npmからインストールできます。Proコンポーネントはnpmでは配布されていません。有効な有料プランでサインインした後、npx @designcodeio/threeui-cliを使ってソースコードとしてプロジェクトにダウンロードします。このプロジェクトはまだ新しく(Communityは2026年8月にオープンソース化されました)、APIが変更される可能性がある点に留意してください。

ThreeUIパッケージのインストール

npmパッケージを1つインストールし、スタイルシートを一度だけインポートします。セットアップ手順はThreeUIのインストールページで解説されています。公開パッケージは、peer dependenciesとしてreactとreact-domのバージョン18以上20未満、およびthreeの0.149以上を指定しています。npm 7以降では、不足しているpeer dependenciesが自動的にインストールされます。Pro CLIにはNode.js 20以降が必要です。

npm install @designcodeio/threeui

スタイルシートをインポートする場所は、使用するフレームワークによって異なります。

フレームワークCSSをインポートする場所
Vitesrc/main.jsx
Next.js App Routerapp/layout.jsx
// src/main.jsx (Vite)
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "@designcodeio/threeui/style.css";
import App from "./App";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

コンポーネントを1つ組み込む

ThreeUIコンポーネントをレンダリングするには、名前を指定してインポートし、JSXに配置します。READMEの例ではAtTheHorizonが使われていますが、このコンポーネントは現在の1.2.0パッケージではエクスポートされていません。そのため本ガイドでは、ヒーローセクションの背後に配置するThree.jsベースのアニメーション背景、EmeraldHorizonBackgroundを使用します。

import { EmeraldHorizonBackground } from "@designcodeio/threeui";

export function Hero() {
  return <EmeraldHorizonBackground />;
}

ルートからのインポートが最もシンプルな方法です。パッケージにはコンポーネントごとのサブパスも用意されており、こちらを使うと開発中に読み込まれるコード量を減らせます。

import { EmeraldHorizonBackground } from "@designcodeio/threeui/components/EmeraldHorizonBackground";

propsはコンポーネントごとに異なります。各コンポーネントのthreeui.comページで確認してください。たとえばBrand Orbsはvariant、size、mode、speed、pausedを受け取ります。propsを推測で指定するのは避けましょう。propsに依存せずにインタラクションを追加したい場合は、外側のコンポーネントでstateを使います。

import { useState } from "react";
import { EmeraldHorizonBackground } from "@designcodeio/threeui";

export function Hero() {
  const [show3D, setShow3D] = useState(true);

  return (
    <section>
      {show3D ? (
        <EmeraldHorizonBackground />
      ) : (
        <img src="/hero-fallback.jpg" alt="Product hero" />
      )}
      <button onClick={() => setShow3D((v) => !v)}>
        {show3D ? "Show static image" : "Show 3D"}
      </button>
    </section>
  );
}

ThreeUIのアセットに関する落とし穴

一部のThreeUIコンポーネントは、完全なHTMLドキュメントをレンダリングします。これらのコンポーネントは、ThreeUIのプレビューサイトと同じ、サイトルート直下の固定パスからランタイムファイルを取得します。たとえばLandscapeは、デフォルトで/landscape.htmlをリクエストします。アプリがそのパスでファイルを配信していない場合、コンポーネントは正しく動作しません。解決方法は2つあります。1つは、必要なファイルをnode_modules/@designcodeio/threeui/lib-dist/assets/からpublicフォルダにコピーする方法です。もう1つは、コンポーネントにsourceUrlまたはassetBaseUrl propがある場合に、ファイルをホストしている場所を指定する方法です。

# Check the network tab for the exact paths the component requests,
# then mirror that layout under public/.
cp -R node_modules/@designcodeio/threeui/lib-dist/assets/. public/
// Only if the component's page lists this prop:
// <SomeComponent assetBaseUrl="/threeui/" />
症状対処法
コンポーネント領域が空のまま表示され、ネットワークタブでルートパス配下に404が出ている必要なアセットをpublic/の対応するパスにコピーする
アセットがCDNまたはサブパスに配置されているコンポーネントが公開している場合はsourceUrlまたはassetBaseUrlを設定する

ThreeUIと素のThree.jsの関係

ThreeUIはレンダラーを隠蔽します。Three.jsベースのコンポーネントでは、シーン、カメラ、レンダラー、マテリアル、アニメーションループがすべてコンポーネント内部に収められています。その他のコンポーネントは生のWebGLまたはCanvas 2Dを使用しています。この利便性と引き換えに失われるのが制御性です。ThreeUIが公開しているのは各コンポーネントのバリアントとコントロールのみであり、その範囲外の変更にはコンポーネントのソースを編集する必要があります。Communityのソースはnode_modules内およびGitHubで参照できます。Pro CLIはProのソースをプロジェクトにコピーします。

公開されているコントロールの範囲を超えるカスタマイズが見込まれる場合は、基盤となるライブラリを学んでおきましょう。基礎についてはBuilding 3D animations using JavaScript and Three.jsで解説されています。

ThreeUIのパフォーマンス:率直な評価

すべてのThreeUIコンポーネントはアニメーションするcanvasを常時実行するため、JavaScript、アセットのダウンロード、GPU時間といったコストが発生します。このコストが最も顕著に表れるのはモバイルGPUです。コンポーネントは遅延読み込みし、prefers-reduced-motionを設定している訪問者に対してはスキップし、3D効果がページに価値をもたらさない場面では静止画像を使いましょう。

React.lazyはdefault exportを前提としているため、named exportをマッピングします。

import { lazy, Suspense, useEffect, useState } from "react";

const EmeraldHorizonBackground = lazy(() =>
  import("@designcodeio/threeui").then((m) => ({ default: m.EmeraldHorizonBackground }))
);

function useReducedMotion() {
  const [reduced, setReduced] = useState(false);
  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    setReduced(mq.matches);
    const onChange = (e) => setReduced(e.matches);
    mq.addEventListener("change", onChange);
    return () => mq.removeEventListener("change", onChange);
  }, []);
  return reduced;
}

function hasWebGL() {
  const canvas = document.createElement("canvas");
  return Boolean(canvas.getContext("webgl2") || canvas.getContext("webgl"));
}

const fallback = <img src="/hero-fallback.jpg" alt="Product hero" />;

export function Hero() {
  const reduced = useReducedMotion();
  const [webgl, setWebgl] = useState(false);
  useEffect(() => setWebgl(hasWebGL()), []);

  if (reduced || !webgl) return fallback;
  return <Suspense fallback={fallback}><EmeraldHorizonBackground /></Suspense>;
}

上記のHeroコンポーネントは、window.matchMediaを通じてprefers-reduced-motionメディア特性を読み取り、HTMLCanvasElement.getContextでコンテキストの有無を確認しています。コンポーネントによっては、reduced motionへの対応を内部で行っているものもあります。たとえばBrand Orbsにはspeedとpaused propsがあり、そのソースコードはprefers-reduced-motionをチェックし、ビューポート外にあるときはアニメーションを一時停止します。各コンポーネントが何に対応しているかは、それぞれのページで確認してください。

Next.jsではnext/dynamicを使用します。ssr: falseオプションはClient Component内でのみ機能し、Server Componentで使用するとNext.jsはエラーをスローします。ブラウザでのみ動作するcanvasコードに対しては、サーバーレンダリングを無効にしておくのが安全なデフォルトです。

"use client";
import dynamic from "next/dynamic";

const EmeraldHorizonBackground = dynamic(
  () => import("@designcodeio/threeui").then((m) => m.EmeraldHorizonBackground),
  { ssr: false, loading: () => <img src="/hero-fallback.jpg" alt="Product hero" /> }
);

export default function Hero() {
  return <EmeraldHorizonBackground />;
}

詳細はNext.jsの遅延読み込みガイドを参照してください。ビルドツールのバンドルアナライザーを実行して、ThreeUIが実際にバンドルにどれだけ追加しているかを確認しましょう。ただし、ラボテストでわかるのは全体像の一部にすぎません。OpenReplayのようなセッションリプレイツールを使えば実際の訪問を観察できるため、低スペックのスマートフォンでヒーローが空白のままになっていたり、カクついたりしている状況を発見できます。既製のコンポーネントでは得られない制御性が必要な場合は、WebGPU vs WebGL: Why the Industry Is Moving Onで低レイヤーの選択肢が解説されています。

ThreeUIパッケージのバージョンは固定しておきましょう。READMEのリリースルールによると、Communityのリリースはメンテナーがレビュー済みの同期プルリクエストをマージした時点で公開され、公開コンポーネント、バリアント、コントロールのいずれかを削除する場合はメジャーバージョンが上がります。AtTheHorizonがその一例で、0.3.0で提供されていましたが、1.xリリースでは削除されています。アップグレード前には必ずリリースノートを確認してください。

まとめ

ThreeUIを使えば、npmインストール1回、スタイルシートのインポート1回、コンポーネント1つで、動作する3Dヒーロー背景を実現できます。ただし、ランタイムアセットの配信には時間を見込んでおく必要があり、スピードと引き換えに制御性を手放すトレードオフがある点も念頭に置いておきましょう。まずはCommunityコンポーネントを1つ、遅延読み込みの境界内でreduced motion用のフォールバックとともにレンダリングするところから始めてください。そのうえでバンドルサイズを計測し、ミドルレンジのスマートフォンでテストしてから、ページに残すかどうかを判断しましょう。

よくある質問

ThreeUIのCommunityコンポーネントを商用プロジェクトで使用できますか?

はい。ThreeUI Communityのアプリケーションコード、コンポーネントコード、およびThreeUIが作成した画像はMITライセンスで提供されており、ライセンス表記を保持する限り商用利用が可能です。同梱されているオープンフォントは引き続きSIL Open Font License 1.1、同梱されているThree.jsのランタイムファイルは引き続きMITライセンスの下にあります。Proコンポーネントは別の有料製品であるため、本番環境に投入する前にProの利用規約を確認してください。

ダウンロードしたコンポーネントを編集した後にThreeUI Pro CLIを再実行するとどうなりますか?

ThreeUI Pro CLIは、--forceフラグを付けない限り、編集済みのファイルを置き換えません(例:npx @designcodeio/threeui-cli add cross-beam --dir ./src --force)。--forceを使用すると編集内容がダウンロードしたソースで上書きされるため、事前に変更をコミットし、手動でマージし直してください。loginやlogoutを含むその他のコマンドやフラグを確認するには、npx @designcodeio/threeui-cli --helpを実行します。

ThreeUI Proコンポーネントがpackage.jsonやnpm updateに表示されないのはなぜですか?

Proコンポーネントはnpmの依存関係ではありません。ThreeUI CLIがソースを直接プロジェクトにコピーするため、package.jsonには記載されず、npm update、npm outdated、およびlockfileベースの依存関係スキャナーでも追跡されません。Proのファイルはベンダリングされたコードとして扱い、バージョン管理に含めたうえで、新しいバージョンが必要になったときはCLIで再ダウンロードしてください。

すべてのThreeUIコンポーネントにWebGLが必要ですか?

いいえ。一部のThreeUIコンポーネントはWebGLではなくCanvas 2Dで描画します。たとえばBrand OrbsのDesignCodeプリセットは、ドットフィールドをCanvas 2Dで描画しています。一律にWebGLチェックを行うと、本来は動作可能なブラウザからこれらのコンポーネントが非表示になってしまいます。getContext('webgl2')またはgetContext('webgl')によるガードは、threeui.comのページでThree.jsまたはWebGLレンダラーを使用していると記載されているコンポーネントにのみ適用してください。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.