問題: なにも設定しないと初期描画のときにカラーモードが反映されない
このように、Chakra UI でなにも設定しないとリロード直後に一瞬ライトモードで表示されます。この記事ではこの問題を解消する方法を紹介します。
tl;dr
ColorModeScript を追加する + initialColorMode をセットする
'use client'; export const ChakraProviderClient = ({ children, initialColorMode, }: PropsWithChildren<{ initialColorMode: 'light' | 'dark'; }>) => ( <ChakraProvider colorModeManager={cookieStorageManager} theme={extendTheme({ config: { initialColorMode } })} > <ColorModeScript type="cookie" /> {children} </ChakraProvider> );
対処1: ColorModeScript を追加する
ColorModeScript をセットすると、最初から背景色や文字色が設定されるようになります。 (コンポーネントの色は一瞬ライトモードで表示されていますが、これは後述の initialColorMode で直ります)
ColorModeScript はこのように使います。
<ColorModeScript type="cookie" />
<ColorModeScript type="cookie" />
はページ読み込み時に cookie を見て body に light/dark クラスを追加してくれます(ColorModeScript の実装)。これによって背景色などがうまく機能するようになります。
対処2: initialColorMode をセットする
initialColorMode をセットすると、リロード時にもボタンの色が設定したカラーモードで表示されるようになります。(ColorModeScript との効果を比較するために、動画ではColorModeScript は抜いています)
initialColorMode をセットしないと、サーバーレンダリング時点では設定で決まった色でレンダリングしてしまいます。そのため、設定したカラーモードで描画するために intialColorMode をセットするようにします。
<ChakraProvider colorModeManager={cookieStorageManager} theme={extendTheme({ config: { initialColorMode } })} > {children} </ChakraProvider>
intialColorMode はサーバーサイドで cookies().get('chakra-ui-color-mode')?.value
で取得できます。あとはこれを ChakraProvider に渡せば完了…なのですが、ここで一つ注意すべき点があります。それは、Client component 内でないと theme={extendTheme({ config: { initialColorMode } })}
を直接渡せないということです。これは、extendTheme で返されるオブジェクトのプロパティに関数が混ざっていて「クライアントコンポーネントに直接関数を渡せない」という制限に引っかかるのが原因です。
これに引っかからないように、クライアントコンポーネントの中で ChakraProvider に theme を渡すようにしてください。
完成形
以上をまとめるとこのようになります。
// ChakraProviderClient.tsx 'use client'; export const ChakraProviderClient = ({ children, initialColorMode, }: PropsWithChildren<{ initialColorMode: 'light' | 'dark'; }>) => ( <ChakraProvider colorModeManager={cookieStorageManager} theme={extendTheme({ config: { initialColorMode } })} > <ColorModeScript type="cookie" /> {children} </ChakraProvider> );
// layout.tsx import { cookies } from 'next/headers'; import { ChakraProviderClient } from './ChakraProviderClient'; export default function Layout({ children, }: { children: React.ReactNode; }) { return ( <html lang='en'> <body> <ChakraProviderClient initialColorMode={cookies().get('chakra-ui-color-mode')?.value}> {children} </ChakraProviderClient> </body> </html> ); }
一応動画も撮りましたが、表示が一切変わらないのでわかりづらい…