Salesforce Developers Blog

Salesforce Mult-Framework を使用した React による UI 開発

Avatar for Hiroyuki InabaHiroyuki Inaba
TDX 2026で発表されたSalesforce Multi-Framework(ベータ版)を使い、ReactでSalesforce UIを開発する手順を解説。Developer Editionのスクラッチ組織でシンプルなUI を作成し、取引先データを表示するまでのステップを公開します。
Salesforce Mult-Framework を使用した React による UI 開発
April 25, 2026

みなさん、こんにちは! TDX 2026 では新機能「Salesforce Multi-Framework」が発表され、Salesforce の UI を React を使って開発することができるようになりました。

注意: 2026年4月時点ではまだ Beta 版です。

この記事では、Developer Edition で作成したスクラッチ組織を使い、内部ユーザーが利用する想定のシンプルなアプリを作成する手順を紹介します。スクラッチ組織の作成方法については、かこのブログ記事「Developer Edition + スクラッチ組織で、複数の開発・検証組織を手にいれる」をご参照ください。ログインして表示言語を日本語化した状態から進めていきます。

事前準備

まずは VS Code で新規に「Standard」で作成したプロジェクトを用意します。

React External App や React Internal App もあるのですが、結構作り込まれたテンプレートが展開されます。構造を理解することがやや大変なので、今回はシンプルなテンプレートからの作成を紹介します。

このあと、スクラッチ組織を作成、もしくは既存のスクラッチ組織に接続します。組織にログインして機能有効化を行ってください。

  • 設定 > アプリケーション > Salesforce Multi-Framework を使用した React 開発 (ベータ)
  • 「Enable Beta」ボタンを押す

この機能はオフにできませんので、必ず Developer Edition や Sandbox などで試すようにしてください。

このあとはローカルの VS Code での操作を進めます。Standard で作成したプロジェクトで、スクラッチ組織に接続した状態にしておきます。

ステップ1: テンプレートを展開

まずターミナルを開き、次のコマンドを実行します。

1sf template generate ui-bundle -n myreactapp -d "./force-app/main/default/uiBundles" -t reactbasic

force-app/main/default/myreactapp ディレクトリに必要なファイルが展開されます。

このディレクトリに移動して、install と run dev を実行します。

1# ディレクトリに移動
2cd force-app/main/default/uiBundles/myreactapp
3
4# 必要なライブラリをインストール
5npm install
6
7# ローカルで実行
8npm run dev

ブラウザを開いて、表示された URL にアクセスしてみます。次のように表示されたら成功です。

ターミナルで Ctrl + C でローカルのサーバーを止めておきます。

ステップ2: ビルドとデプロイ

2026年4月25日時点ですが、graphqlClient.ts でエラーを検出しています。当該ファイルの13行目を次のように修正します。

1修正前: const response = await data.graphql?.<TData, TVariables>(query, variables);
2修正後: const response = await data.graphql?.<TData, TVariables>({query, variables});

それではビルドを行います。同じフォルダで次のコマンドを実行します。

1npm run build

エラーが出なければ成功です。

それでは組織にデプロイしてみましょう。次のコマンドを実行します。(スクラッチ組織の場合のコマンドです)

1sf project deploy start

Status が Succeeded で完了すれば成功です。

ステップ3: 動作確認

それでは組織にデプロイされた React アプリを見てみましょう。

  • アプリケーションラン���ャー > Myreactapp

先ほどのローカルテストと同じように表示されれば成功です。

ステップ4: 取引先のデータを表示できるように修正

pages ディレクトリの下に「Accounts.tsx」ファイルを作成します。(Agentforce Vibes に作ってもらいましたので、切り詰めればもっとシンプルにできるかもしれません。)

1import { useEffect, useState } from 'react';
2import { executeGraphQL } from '@/api/graphqlClient';
3import {
4  Table,
5  TableBody,
6  TableCell,
7  TableHead,
8  TableHeader,
9  TableRow,
10} from '@/components/ui/table';
11import { Skeleton } from '@/components/ui/skeleton';
12import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
13
14interface AccountNode {
15  Id: string;
16  Name: { value: string };
17  Website: { value: string | null };
18}
19
20interface AccountsData {
21  uiapi: {
22    query: {
23      Account: {
24        edges: Array<{
25          node: AccountNode;
26        }>;
27      };
28    };
29  };
30}
31
32const ACCOUNTS_QUERY = `query GetAccounts {
33  uiapi {
34    query {
35      Account {
36        edges {
37          node {
38            Id
39            Name {
40              value
41            }
42            Website {
43              value
44            }
45          }
46        }
47      }
48    }
49  }
50}`;
51
52export default function Accounts() {
53const [accounts, setAccounts] = useState<AccountNode[]>([]);
54const [loading, setLoading] = useState(true);
55const [error, setError] = useState<string | null>(null);
56
57  useEffect(() => {
58const fetchAccounts = async () => {
59try {
60        setLoading(true);
61        setError(null);
62const data = await executeGraphQL<AccountsData, undefined>(ACCOUNTS_QUERY);
63const accountNodes = data.uiapi.query.Account.edges.map(edge => edge.node);
64        setAccounts(accountNodes);
65      } catch (err) {
66        setError(err instanceof Error ? err.message : 'Failed to fetch accounts');
67        console.error('Error fetching accounts:', err);
68      } finally {
69        setLoading(false);
70      }
71    };
72    fetchAccounts();
73  }, []);
74
75if (loading) {
76return (
77      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
78        <h1 className="text-3xl font-bold text-gray-900 mb-6">取引先一覧</h1>
79        <div className="space-y-2">
80          <Skeleton className="h-12 w-full" />
81          <Skeleton className="h-12 w-full" />
82          <Skeleton className="h-12 w-full" />
83          <Skeleton className="h-12 w-full" />
84          <Skeleton className="h-12 w-full" />
85        </div>
86      </div>
87    );
88  }
89
90if (error) {
91return (
92      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
93        <h1 className="text-3xl font-bold text-gray-900 mb-6">取引先一覧</h1>
94        <Alert variant="destructive">
95          <AlertTitle>エラー</AlertTitle>
96          <AlertDescription>{error}</AlertDescription>
97        </Alert>
98      </div>
99    );
100  }
101
102return (
103    <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
104      <div className="mb-6">
105        <h1 className="text-3xl font-bold text-gray-900">取引先一覧</h1>
106                  全{accounts.length}件の取引先が登録されています
107      </div>
108      {accounts.length === 0 ? (
109        <Alert>
110          <AlertTitle>取引先がありません</AlertTitle>
111          <AlertDescription>
112            現在、登録されている取引先はありません。
113          </AlertDescription>
114        </Alert>
115      ) : (
116        <div className="border rounded-lg overflow-hidden">
117          <Table>
118            <TableHeader>
119              <TableRow>
120                <TableHead className="font-semibold">ID</TableHead>
121                <TableHead className="font-semibold">取引先名</TableHead>
122                <TableHead className="font-semibold">ウェブサイト</TableHead>
123              </TableRow>
124            </TableHeader>
125            <TableBody>
126              {accounts.map(account => (
127                <TableRow key={account.Id}>
128                  <TableCell className="font-mono text-sm">
129                    {account.Id}
130                  </TableCell>
131                  <TableCell className="font-medium">
132                    {account.Name.value}
133                  </TableCell>
134                  <TableCell>
135                    {account.Website.value ? (
136                      <a
137                         href={account.Website.value}
138                         target="_blank" rel="noopener noreferrer"
139className="text-blue-600 hover:underline">
140                        {account.Website.value}
141                      </a>
142                    ) : ('—')}
143                  </TableCell>
144                </TableRow>
145              ))}
146            </TableBody>
147          </Table>
148        </div>
149      )}
150    </div>
151  );
152}

routes.tsx ファイルを次の内容に置き換えます。

1import type { RouteObject } from 'react-router';
2import AppLayout from '@/appLayout';
3import Home from './pages/Home';
4import Accounts from './pages/Accounts';
5import NotFound from './pages/NotFound';
6
7export const routes: RouteObject[] = [
8  {
9    path: '/',
10    element: <AppLayout />,
11    children: [
12      {
13        index: true,
14        element: <Home />,
15        handle: { showInNavigation: true, label: 'Home' },
16      },
17      {
18        path: 'accounts',
19        element: <Accounts />,
20        handle: { showInNavigation: true, label: '取引先' },
21      },
22      {
23        path: '*',
24        element: <NotFound />,
25      },
26    ],
27  },
28];

ステップ5: 再ビルドとデプロイ

ファイルの修正が完了したら、再度ビルドを行い組織にデプロイします。

1# ビルドの実行
2npm run build
3
4# デプロイの実行
5sf project deploy start
6
7# もしソースファイルエラーやコンフリクトが発生した場合(コマンドを実行するディレクトリによって --source-dir の引数は要調整)
8sf project deploy start --source-dir . --ignore-conflicts

Status が Succeeded で完了すれば成功です。

ステップ6: 動作確認

スクラッチ組織の場合、取引先にはデータが何もないのでいくつか作成しておきます。

再度 Myreactapp を表示します。画面右上のメニューアイコンを押すと「取引先」選択肢が出現し、選択すると取引先の一覧が表示されれば成功です。

おわりに

今までは Lightnign Experience & Lightning Web Components と Visualforce に頼っていた Web ブラウザへの UI 提供ですが、React にも対応したことで、そのデータの表現力が格段に向上しました。いわゆる社内向けの UI はもちろん、Experience Cloud での社外向けサイトでの利用も可能です。正式リリースまで今しばらくお待ちください。

今回はすべて手動での作業手順でご紹介しましたが、もちろんバイブコーディングで開発を進めていくこともできます。Agentforce Vibes はもちろん、その他のコーディング支援ツールでも利用できるようにスキルを公開していたりしますので、ぜひご活用ください。

SFDX プロジェクトの新規作成を行う際に、React Internal App を選ぶとより作り込まれたテンプレートが展開されます。Agentforce で作成したエージェントとのチャットも行える UI も組み込まれています。こちらもぜひ試してみてください。

参考資料

More Blog Posts

Build with React, Run on Salesforce: Introducing Salesforce Multi-Framework

Build with React, Run on Salesforce: Introducing Salesforce Multi-Framework

Salesforce Multi-Framework is in open beta. Build native React apps on the Agentforce 360 Platform with GraphQL, Apex, and built-in platform security.April 15, 2026