🦔

NextJS(App Router)のバグを踏んだ

2024/04/26に公開

はじめに

App RouterのRoute Groups機能のバグの記録です。
Route Groups機能を利用していると、サーバー側でのレンダリングに必要なクライアント側のデータがサーバー側に渡されていないというバグです。
環境:NextJS v14.2.1

経緯

元々、MUIを使って構築していたサイトをYamada UIに入れ替えました。
MUIの時もそうだったのですが、サイトにアクセスした際に一瞬スタイルが崩れるという事象が発生していたので、応急処置でuseEffectかましてました。

src/app/provider.tsx
const [show_screen, setShowScreen] = useState(false)

useEffect(() => {
    setShowScreen(true)
}, [])

return (
    <>
        <ColorModeScript type="cookie" nonce="testing" />
        <ThemeSchemeScript type="cookie" nonce="testing" />
        <UIProvider
            theme={theme}
            colorModeManager={{ ...colorModeManager }.cookieStorage}
        >
            {show_screen ? children : null}
        </UIProvider>
    </>
)
一瞬スタイルが当たらず、ちょっと経って正常に表示される図

最初は一瞬スタイルが当たらないところがある。

ちょっと待つと正常に表示される。

検証

いろいろと検証をした結果、Route Groupsを使っていることが原因みたいだということがわかりました。
以下のようなフォルダ構成にしており、rootのnot-found.tsxに含めたくないlayoutがあったため、(grid)というグループを作っています。
/にアクセスした際に表示されるのはsrc/app/(grid)/page.tsxになります。

src
├─app
│  ├─(grid)
│  │  ├─about
│  │  ├─admin
│  │  ├─archives
│  │  │  └─[id]
│  │  ├─postForm
│  │  ├─search
│  │  ├─tag
│  │  │   └─[tagId]
│  │  ├─layout.tsx
│  │  ├─loading.tsx
│  │  └─page.tsx
│  ├─layout.tsx
│  └─not-found.tsx
└─theme
   ├─components
   ├─styles
   ├─tokens
   ├─config.ts
   ├─semantics.ts
   └─index.ts

結論から言うと、page.tsx(grid)の外に出したら直りました。

src
├─app
│  ├─(grid)
│  ├─page.tsx
│  ├─layout.tsx
│  └─not-found.tsx
└─theme
   ├─components
   ├─styles
   ├─tokens
   ├─config.ts
   ├─semantics.ts
   └─index.ts

原因の考察

Yamada UIを使ってカスタマイズしているので、スタイリングはtheme/以下で管理しています。
theme/index.tsはクライアント側で必要なため、"use client"をつけています。

theme/index.ts
"use client"
import { extendConfig, extendTheme, UsageTheme } from "@yamada-ui/react"
import { semantics } from "./semantics"
import styles from "./styles"
import { customConfig } from "./config"
import components from "./components"
import tokens from "./tokens"

const customTheme: UsageTheme = {
    styles,
    components,
    semantics,
    ...tokens,
}

export const theme = extendTheme(customTheme)()
export const config = extendConfig(customConfig)

export default theme

このtheme/index.tsはもちろんサーバー側でレンダリングする際も必要なため、サーバー側にも渡されるべきです。
しかし、(grid)配下のpage.tsxに渡されておらず、そのため一瞬スタイルが当たっていない状態が発生していると考えられます。
サーバー側ではスタイルが当たっていないコンポーネントがレンダリングされ、クライアント側でスタイルが当たっているような動作になっています。

まとめ

コードまで読んでいるわけではないですが、少なくともこの構成は変えないといけません。
(grid)のさらに配下や、root以外でも同様にバグがあるのかなど詳細な検証は行っていません。

補足

NextJSはReact→NextJS→Clientのようにデータが受け渡されるはずですが、Route Groups機能を使ってグルーピングした配下のページにクライアントコンポーネントのデータが渡されていない可能性があります。(要検証)

追記

Route Groups以外にも同様の動作を確認したので、Route Groups機能のバグじゃなさそう

GitHubで編集を提案

Discussion