空雲 Blog

Eye catchNext.jsの Client Componentで async/await を使う

publication: 2025/09/22
update:2025/09/22

クライアントコンポーネントでも async/await は使える

クライアントコンポーネントをサーバ上で動かす際、async/await が使えないという誤解があります
その誤解を解くために最小限のサンプルを用意しました

このサンプルではサーバ上で非同期データを取得し HTML で出力、ブラウザ上で Hydration して動作します

重要な点は、throw property.promiseの部分です。ここで Promise が解決された際に、コンポーネントが再レンダリングされます。この機能は React のrenderToReadableStream実行時の標準動作です。<Suspense>で囲むと、非同期待ちではなくストリーミングになってしまうので注意が必要です

クライアントコンポーネントは Hydration 時、サーバ側で持っていたデータを失います。サーバコンポーネントと連携する際は、このデータの引き渡しが自動で生成されますが、クライアントコンポーネント単独の場合は、自分でやり取りする必要があります。この引き渡し作業は自動で生成される内容とほぼ同じです。

  • サンプルコード

https://github.com/SoraKumo001/next-client-async

  • サンプルを Vercel にデプロイしたもの

https://next-client-async.vercel.app/

1"use client";
2import { useRef, useSyncExternalStore, type FC } from "react";
3
4const DATA_NAME = "DATA_TEST";
5
6const Weather: FC<{
7 property: { data?: unknown; promise?: Promise<void> };
8}> = ({ property }) => {
9 const data = useSyncExternalStore(
10 () => () => {},
11 () => property.data,
12 () => {
13 if (!property.data) {
14 if (typeof window !== "undefined") {
15 // ブラウザ側でHydration直後にデータを受け取る
16 const node = document.getElementById(DATA_NAME);
17 property.data = JSON.parse(node.innerHTML);
18 } else if (!property.promise) {
19 // サーバ(物理)動作時に天気予報データを取得
20 const getWeather = async () => {
21 const result = await fetch(
22 `https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json`
23 );
24 property.data = await result.json();
25 };
26 property.promise = getWeather();
27 // Promiseを解決後、再レンダリングさせる
28 throw property.promise;
29 }
30 }
31 return property.data;
32 }
33 );
34 return (
35 <>
36 {/* クライアントへ渡すデータをHTML化 */}
37 <script
38 id={DATA_NAME}
39 type="application/json"
40 dangerouslySetInnerHTML={{
41 // データをJSON文字列に変換と、HTML用にエスケープ
42 __html: JSON.stringify(data).replace(/</g, "\\u003c"),
43 }}
44 />
45 {/* 確認表示用 */}
46 <pre>{JSON.stringify(data, null, 2)}</pre>
47 </>
48 );
49};
50
51const Page = () => {
52 const property = useRef<{
53 data?: unknown;
54 promise?: Promise<void>;
55 }>({}).current;
56 return <Weather property={property} />;
57};
58
59export default Page;

まとめ

Next.js のクライアントコンポーネントは、サーバ上で async/await を使うことができます。そこで取得したデータは、Server Component と同様に HTML に埋め込んでブラウザ側に引き渡し、Hydration 後も利用可能です。この動作は React の標準動作内で実行されるため、クライアントコンポーネントに限らず、Next.js の PagesRouter や React Router など、他の環境でも同様に利用できます。