Next.js / TypeScriptでThree.js(react-three-fiber)を使うまで
TypeScriptを使ったNext.js環境で、Three.jsを使えるようにしていくまでの課程です。
汎用的に使えるようにできるだけ最低限の構成を目指しています。
Three.jsはあまり触れたことがないので私自身よくわかっていないのが正直なところです。
(ご指摘あればお気軽にお願いいたします。)
Three.jsのReactWrapper(react-three-fiber)を使いました。
Hooksなどの用意もあって、オーバーヘッドもない(公式見解)とのことなので、簡潔なコードを書くためにも導入しました。
途中ビルドエラーもあったので、解消方法も記しておきます。
バージョン情報
| パッケージ | バージョン | 
|---|---|
| next | 10.2.3 | 
| typescript | 4.3.2 | 
| @react-three/fiber | ^7.0.1 | 
| react | 17.0.2 | 
パッケージ追加
Next.jsはインストールしておく
yarn add three @types/three @react-three/fiber
React (Typescript) + react-three-fiber + three-vrmでVRMモデルを表示してみる
こちらの記事を参考にサンプルページを作成
ページを作成
import { Canvas, useFrame } from "@react-three/fiber";
import { Mesh } from "three";
import React, { useRef } from "react";
import Controls from "../utils/Controls";
const Thing = () => {
  const ref = useRef({} as Mesh);
  useFrame(() => (ref.current.rotation.z += 0.01));
  return (
    <mesh
      ref={ref}
      onClick={(e) => console.log("click")}
      onPointerOver={(e) => console.log("hover")}
      onPointerOut={(e) => console.log("unhover")}
    >
      <planeBufferGeometry attach="geometry" args={[1, 1]} />
      <meshBasicMaterial
        attach="material"
        color="hotpink"
        opacity={0.5}
        transparent
      />
    </mesh>
  );
};
const Three: React.FC = React.memo(() => {
  return (
    <div style={{ width: "100vw", height: "100vh" }}>
      <Canvas>
        <Thing />
        <Controls />
        <gridHelper />
      </Canvas>
    </div>
  );
});
export default Three;
import React, { useRef } from "react";
import {
  extend,
  ReactThreeFiber,
  useThree,
  useFrame,
} from "@react-three/fiber";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
extend({ OrbitControls });
declare global {
  namespace JSX {
    interface IntrinsicElements {
      readonly orbitControls: ReactThreeFiber.Object3DNode<
        OrbitControls,
        typeof OrbitControls
      >;
    }
  }
}
export default function Controls(
  props: ReactThreeFiber.Object3DNode<OrbitControls, typeof OrbitControls>
) {
  const {
    camera,
    gl: { domElement },
  } = useThree();
  const controls = useRef({} as OrbitControls);
  useFrame(() => controls.current.update());
  return (
    <orbitControls {...props} ref={controls} args={[camera, domElement]} />
  );
}
ここでちょっとつまづき
OrbitControlマウスコントロールモジュールをimportしてみたが、Next.js / Typescript環境では以下のエラーが出てビルドエラーが発生した
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ../node_modules/three/examples/jsm/controls/OrbitControls.js
require() of ES modules is not supported.
require() of ../node_modules/three/examples/jsm/controls/OrbitControls.js from ../.next/server/pages/three.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename OrbitControls.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ../node_modules/three/examples/jsm/package.json.
    at new NodeError (node:internal/errors:363:5)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1126:13)
    at Module.load (node:internal/modules/cjs/loader:989:32)
    at Function.Module._load (node:internal/modules/cjs/loader:829:14)
    at Module.require (node:internal/modules/cjs/loader:1013:19)
    at require (node:internal/modules/cjs/helpers:93:18)
    at Object.three/examples/jsm/controls/OrbitControls (../.next/server/pages/three.js:77:18)
    at __webpack_require__ (../.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./utils/Controls.tsx:11:99)
    at Object../utils/Controls.tsx (../.next/server/pages/three.js:33:1)
    at __webpack_require__ (../.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./pages/three.tsx:8:73)
    at Object../pages/three.tsx (../.next/server/pages/three.js:22:1)
    at __webpack_require__ (../.next/server/webpack-runtime.js:33:42)
    at __webpack_exec__ (../.next/server/pages/three.js:87:52)
    at ../.next/server/pages/three.js:88:28 {
  code: 'ERR_REQUIRE_ESM'
}
ビルドエラー解消
ES Moduleで書かれたライブラリをNext.jsでimportすると ERR_REQUIRE_ESM エラーが起こるらしい
しばらく調べると、ESMモジュールをトランスパイルできるライブラリがあるらしいので使ってみる
yarn add -D next-transpile-modules
const withTM = require('next-transpile-modules')(['three'])
module.exports = withTM()
参考 :
ビルドすると今度は、以下のようなwebpack周りのエラーがでるので、
const isLocal=request.startsWith('.')||// Always check for unix-style path, as webpack sometimes
webpack 5を使うように設定する
const withTM = require('next-transpile-modules')(['three'])
module.exports = withTM({
  future: {
    webpack5: true
  }
})
Next.jsでThreeを使ってOrbitControlを実装して、ビルド出来た

参考記事
React (Typescript) + react-three-fiber + three-vrmでVRMモデルを表示してみる
超楽しくてカッコいい、Reactで始めるThree.js入門




Discussion
ここから直接Controls使った方が楽ですよ!
ありがとうございます😊
試してみます💪