2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2025年版:Rust + WASM + React のセットアップガイド

2
Last updated at Posted at 2025-12-03

導入

WASM(WebAssembly)は、ブラウザ上で高速にプログラムを動作させるためのバイナリのフォーマットの一種です。C/C++/Rustなどの言語で書かれたコードをブラウザ上で動作させるために、このバイナリフォーマットにコンパイルします。この記事では、RustとWASMを使って、Reactアプリケーションを構築する方法を紹介します。

この記事で触れる点

  • Rust + WASM + React環境構築
  • Vite + wasm-packでの高速セットアップ
  • RustコードをReactから呼び出す基本のパターン実装

Rust × WebAssembly の利点

  • 高速
  • 安全
  • 軽量
  • メモリ安全性

特に、画像処理やアルゴリズム、ゲームロジックなど、計算量の多い処理で、ブラウザ上で高速に動作させることができ、技術的な期待値が高いと感じています。

また、Viteと組み合わせることで、高速な開発体験を得られ、エコシステムも充実していると感じています。


使用技術スタック(2025年11月時点)

フロントエンド

  • React 19.2
  • Vite 7.2.*
  • 言語: TypeScript

WebAssembly

  • 言語: Rust 1.91.*
  • wasm-pack 0.13.1: RustをWASMに変換するツール
  • wasm-bindgen 0.2.84: RustとJavaScript間のブリッジ

Viteプラグイン

  • vite-plugin-wasm 3.5.0: WASMモジュールの統合
  • vite-plugin-top-level-await 1.6.0: async/awaitサポート

環境構築

Requirements

以下のインストールを前提条件とします。

  • Node: v24.11.*
  • cargo: v1.91.*

インストールされていない場合は各種インストールをしてください。

1. wasm-packのインストール

cargo install wasm-pack

インストールの確認:

wasm-pack --version
# wasm-pack 0.13.1 みたいな表示がでればOK

2. プロジェクトディレクトリの準備

まず、プロジェクト全体を管理するディレクトリを作成します:

mkdir [project-name]
cd [project-name]

3. Reactプロジェクトの作成(Vite使用)

npm create vite@latest frontend

対話形式で以下の選択を行います:
基本的には自由ですが、ここでは、以下のように選択しました。

  • Select a framework: React を選択
  • Select a variant: TypeScript を選択
  • Use rolldown-vite (Experimental)?: No を選択
  • Install with npm and start now?: Yes を選択

セットアップが完了したら、開発サーバーをブラウザで確認できます。ブラウザで 表示のlocalhostを開くと、Viteのデフォルトのページが表示されます。 Ctrl+C で停止させます。

次に、依存関係をインストールします。

cd frontend
npm install

4. Vite用WASMプラグインのインストール

frontendディレクトリで、WASMを扱うためのプラグインをインストールします:

npm install -D vite-plugin-wasm vite-plugin-top-level-await
cd ..

5. Vite設定の更新

frontend/vite.config.tsを以下のように書き換えてみます。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
  plugins: [
    react(),
    wasm(),
    topLevelAwait()
  ],
  build: {
    target: 'esnext'
  }
})

ポイント

  • wasm(): WASMファイルを処理
  • topLevelAwait(): トップレベルでのasync/awaitをサポート
  • target: 'esnext': 最新のES機能を有効化

6. Rustプロジェクトの作成

wasm-packのテンプレート機能を使って構築します。

wasm-pack new [wasm-project-name]

7. Cargo.tomlの確認と調整

生成された[wasm-project-name]/Cargo.tomlを確認してください。テンプレートですでに適切な設定がされていますが、最適化のため以下を追加します:

[package]
name = "[wasm-project-name]"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2"

console_error_panic_hook = { version = "0.1.7", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.34"

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1

追加設定の説明:

  • opt-level = "z": WASMファイルサイズを最小化
  • lto = true: 最適化を強化(ビルド時間は長くなるが、実行速度向上)
  • codegen-units = 1: 並列コンパイルを無効化して最適化を優先
  • console_error_panic_hook: panicメッセージをブラウザコンソールに表示(デバッグに便利)

動作確認1:デフォルトのWASMを表示

まず、テンプレートで作成されたサンプルコードが動作することを確認してみます。

1. デフォルトのWASMをビルド

wasm-pack newで生成された[wasm-project-name]/src/lib.rsには、すでにサンプルの関数が含まれています。

[wasm-project-name]/src/lib.rs

mod utils;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, [wasm-project-name]!");
}

このまま一度ビルドしてみます

cd [wasm-project-name]
wasm-pack build --target web --out-dir ../frontend/src/wasm
cd ..

2. Reactで呼び出してみる

frontend/src/App.tsxを以下のように編集します

frontend/src/App.tsx

import { useState, useEffect } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import init, { greet } from './wasm/[wasm_project_name]'
// ↑ Cargo.toml の name をスネークケースに変換(例: chess-wasm → chess_wasm)

function App() {
  const [wasmReady, setWasmReady] = useState(false)

  useEffect(() => {
    // WASM モジュールを初期化
    init()
      .then(() => {
        setWasmReady(true)
      })
      .catch((err) => {
        console.error('WASM初期化エラー:', err)
      })
  }, [])

  const handleClick = () => {
    if (wasmReady) {
      greet()
    }
  }

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>

        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>

      <h1>Vite + React + WASM</h1>

      {wasmReady ? (
        <div className="card">
          <button onClick={handleClick}>Rustの関数を呼び出す</button>
        </div>
      ) : (
        <p>Loading...</p>
      )}
    </>
  )
}

export default App

ポイント

  • init(): WASMバイナリを読みこんで、メモリを初期化(wasm-bindgenの自動生成)
  • wasmReady: 初期化完了フラグ(完了前の関数呼び出しエラーを防ぐ)

3. 開発サーバーを起動

npm run dev

以下のような画面が表示され、ボタンを押すとalertが表示されるはずです。

スクリーンショット 2025-12-03 053530.png


動作確認2:Rustに関数を追加

次に、独自の関数を追加して、変更が反映されることを確認します。

1. 簡単な計算関数を追加

[wasm-project-name]/src/lib.rsを以下のように編集

[wasm-project-name]/src/lib.rs

mod utils;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, chess-wasm!");
}

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[wasm_bindgen]
pub fn message(name: &str) -> String {
    format!("Hello, {}!", name)
}

編集し終わったら、再ビルドしてください。

2. React側で指定した関数を使う

frontend/src/App.tsxを更新

frontend/src/App.tsx

import { useState, useEffect } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import init, { greet, add, message } from './wasm/[wasm_project_name]'
// ↑ Cargo.tomlのnameをスネークケースに変換(例: chess-wasm → chess_wasm)

function App() {

  const [wasmReady, setWasmReady] = useState(false)
  const [addResult, setAddResult] = useState<number | null>(null)
  const [messageResult, setMessageResult] = useState<string>('')

  useEffect(() => {
    // WASMモジュールを初期化
    init().then(() => {
      setWasmReady(true)
    }).catch(err => {
      console.error('WASM初期化エラー:', err)
    })
  }, [])

  const handleClick = () => {
    if (wasmReady) {
      greet()
    }
  }

  const handleClickAdd = () => {
    if (wasmReady) {
      const result = add(1, 2)
      console.log(result)
      setAddResult(result)
    }
  }

  const handleClickMessage = () => {
    if (wasmReady) {
      const result = message("Rust")
      console.log(result)
      setMessageResult(result)
    }
  }


  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React + WASM</h1>
      {wasmReady ? (
        <div className="card">
          <button onClick={handleClick}>
            Rustの関数を呼び出す
          </button>
        </div>
      ) : (
        <p>Loading...</p>
      )}
      <div className="card">
        <button onClick={handleClickAdd}>
          add
        </button>
        <p>結果: {addResult !== null ? addResult : '未実行'}</p>
      </div>
      <div className="card">
        <button onClick={handleClickMessage}>
          message
        </button>
        <p>結果: {messageResult || '未実行'}</p>
      </div>
    </>
  )
}

export default App

3. 開発サーバーを起動

開発サーバーを起動し、各ボタンをクリックして動作を確認しましょう。

以下のように表示されれば、成功です。

スクリーンショット 2025-12-03 054417.png


最後に

この記事では、2025年の最新技術スタックを使って、Rust + WebAssembly + Reactの開発環境を構築しました。

今後の記事では、実際のパフォーマンスの比較や、このモノレポをベースとしたゲームロジックの開発の記事を投稿しようと考えております。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?