Skip to content

Commit 58831f0

Browse files
authored
Add InitColorSchemeScript for Next.js App Router (#42247)
1 parent 1ed469d commit 58831f0

32 files changed

+249
-118
lines changed

docs/data/joy/customization/dark-mode/dark-mode.md

+16-27
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ When you change the `defaultMode` to another value, you must clear the local sto
1212

1313
{{"demo": "DarkModeByDefault.js"}}
1414

15-
For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function:
15+
For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `InitColorSchemeScript` component:
1616

1717
```js
18-
getInitColorSchemeScript({ defaultMode: 'dark' });
18+
<InitColorSchemeScript defaultMode="dark" />
1919
```
2020

2121
## Matching device's preference
@@ -28,10 +28,10 @@ import { CssVarsProvider } from '@mui/joy/styles';
2828
<CssVarsProvider defaultMode="system">...</CssVarsProvider>;
2929
```
3030

31-
For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function:
31+
For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `InitColorSchemeScript` component:
3232

3333
```js
34-
getInitColorSchemeScript({ defaultMode: 'system' });
34+
<InitColorSchemeScript defaultMode="system" />
3535
```
3636

3737
### Identify the system mode
@@ -121,23 +121,23 @@ If you try to render your UI based on the server, before mounting on the client,
121121

122122
### Avoiding screen flickering
123123

124-
To [prevent the UI from flickering](/joy-ui/main-features/dark-mode-optimization/#the-problem-flickering-on-first-load), apply `getInitColorSchemeScript()` before the main application script-it varies across frameworks:
124+
To [prevent the UI from flickering](/joy-ui/main-features/dark-mode-optimization/#the-problem-flickering-on-first-load), apply `<InitColorSchemeScript />` before the main application script-it varies across frameworks:
125125

126126
### Next.js Pages Router
127127

128128
To use the Joy UI API with a Next.js project, add the following code to the custom [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document) file:
129129

130130
```jsx
131131
import Document, { Html, Head, Main, NextScript } from 'next/document';
132-
import { getInitColorSchemeScript } from '@mui/joy/styles';
132+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
133133

134134
export default class MyDocument extends Document {
135135
render() {
136136
return (
137137
<Html data-color-scheme="light">
138138
<Head>...</Head>
139139
<body>
140-
{getInitColorSchemeScript()}
140+
<InitColorSchemeScript />
141141
<Main />
142142
<NextScript />
143143
</body>
@@ -149,34 +149,23 @@ export default class MyDocument extends Document {
149149

150150
### Next.js App Router
151151

152-
To use the Joy UI API with a Next.js project with the App Router, create a separate [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to utilize the [`getInitColorSchemeScript`](https://mui.com/joy-ui/main-features/dark-mode-optimization/#the-solution-css-variables) function:
153-
154-
```jsx title="colorInit.js"
155-
'use client';
156-
157-
import { getInitColorSchemeScript } from '@mui/joy/styles';
158-
159-
export default function ColorInit() {
160-
return <>{getInitColorSchemeScript()}</>;
161-
}
162-
```
163-
164-
Now, you can use the it in your [`app/layout.js`](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts) file in order to prevent flickering:
152+
To use the Joy UI API with a Next.js project with the App Router, add the following code to the [`app/layout.js`](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts) file in order to prevent flickering:
165153

166154
```jsx title="layout.js"
167-
import ColorInit from './colorInit';
168-
import { CssBaseline, CssVarsProvider } from '@mui/joy';
155+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
156+
import { CssVarsProvider } from '@mui/joy/styles';
157+
import CssBaseline from '@mui/joy/CssBaseline';
169158

170159
export default function RootLayout({ children }) {
171160
return (
172161
<html lang="en" suppressHydrationWarning={true}>
173-
<CssVarsProvider>
174-
<body>
162+
<body>
163+
<InitColorSchemeScript />
164+
<CssVarsProvider>
175165
<CssBaseline />
176-
<ColorInit />
177166
{children}
178-
</body>
179-
</CssVarsProvider>
167+
</CssVarsProvider>
168+
</body>
180169
</html>
181170
);
182171
}

docs/data/joy/main-features/dark-mode-optimization/dark-mode-optimization.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@ Solving this problem required us to take a novel approach to styling and theming
2727

2828
Thanks to Joy UI's built-in support for CSS variables, your app can render all of its color schemes at build time, so that the user's preference can be injected _before_ the DOM is rendered in the browser.
2929

30-
Joy UI provides the `getInitColorSchemeScript()` function to make this flash-free dark mode possible with React frameworks like Next.js or Remix.
30+
Joy UI provides the `InitColorSchemeScript` component to make this flash-free dark mode possible with React frameworks like Next.js or Remix.
3131
This function must be placed before the main script so it can apply the correct stylesheet before your components are rendered.
3232

3333
The code snippet below shows how this works with the Next.js Pages Router:
3434

3535
```jsx
3636
import Document, { Html, Head, Main, NextScript } from 'next/document';
37-
import { getInitColorSchemeScript } from '@mui/joy/styles';
37+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
3838

3939
export default class MyDocument extends Document {
4040
render() {
4141
return (
4242
<Html data-color-scheme="light">
4343
<Head>...</Head>
4444
<body>
45-
{getInitColorSchemeScript()}
45+
<InitColorSchemeScript />
4646
<Main />
4747
<NextScript />
4848
</body>

docs/data/material/customization/css-theme-variables/configuration.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,10 @@ function App() {
286286
}
287287
```
288288

289-
For a server-side application, provide the same value to [`getInitColorSchemeScript()`](/material-ui/customization/css-theme-variables/usage/#server-side-rendering):
289+
For a server-side application, provide the same value to [`InitColorSchemeScript`](/material-ui/customization/css-theme-variables/usage/#server-side-rendering):
290290

291291
```js
292-
getInitColorSchemeScript({
293-
defaultMode: 'dark',
294-
});
292+
<InitColorSchemeScript defaultMode="dark" />
295293
```
296294

297295
:::warning

docs/data/material/customization/css-theme-variables/usage/usage.md

+26-8
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,42 @@ The structure of this object is nearly identical to the theme structure, the onl
115115

116116
## Server-side rendering
117117

118-
Place `getInitColorSchemeScript()` before the `<Main />` tag to prevent the dark-mode SSR flickering during the hydration phase.
118+
Place `<InitColorSchemeScript />` before the `<Main />` tag to prevent the dark-mode SSR flickering during the hydration phase.
119+
120+
### Next.js App Router
121+
122+
Add the following code to the [root layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) file:
123+
124+
```jsx title="app/layout.js"
125+
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
126+
127+
export default function RootLayout({ children }) {
128+
return (
129+
<html lang="en">
130+
<body>
131+
<InitColorSchemeScript /> {/* must come before the <main> element */}
132+
<main>{children}</main>
133+
</body>
134+
</html>
135+
);
136+
}
137+
```
119138

120139
### Next.js Pages Router
121140

122141
Add the following code to the custom [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document) file:
123142

124-
```jsx
143+
```jsx title="pages/_document.js"
125144
import Document, { Html, Head, Main, NextScript } from 'next/document';
126-
import { getInitColorSchemeScript } from '@mui/material/styles';
145+
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
127146

128147
export default class MyDocument extends Document {
129148
render() {
130149
return (
131150
<Html data-color-scheme="light">
132151
<Head>...</Head>
133152
<body>
134-
{getInitColorSchemeScript()}
153+
<InitColorSchemeScript /> {/* must come before the <Main> element */}
135154
<Main />
136155
<NextScript />
137156
</body>
@@ -158,7 +177,7 @@ const StyledComponent = styled('button')(({ theme }) => ({
158177

159178
## API
160179

161-
### `<CssVarsProvider>` props
180+
### `<CssVarsProvider>` &nbsp;props
162181

163182
- `defaultMode?: 'light' | 'dark' | 'system'` - Application's default mode (`light` by default)
164183
- `disableTransitionOnChange : boolean` - Disable CSS transitions when switching between modes
@@ -171,10 +190,9 @@ const StyledComponent = styled('button')(({ theme }) => ({
171190
- `mode: string` - The user's selected mode
172191
- `setMode: mode => {…}` - Function for setting the `mode`. The `mode` is saved to internal state and local storage; if `mode` is null, it will be reset to the default mode
173192

174-
### `getInitColorSchemeScript: (options) => React.ReactElement`
175-
176-
**options**
193+
### `<InitColorSchemeScript>` &nbsp;props
177194

178195
- `defaultMode?: 'light' | 'dark' | 'system'`: - Application's default mode before React renders the tree (`light` by default)
179196
- `modeStorageKey?: string`: - localStorage key used to store application `mode`
180197
- `attribute?: string` - DOM attribute for applying color scheme
198+
- `nonce?: string` - Optional nonce passed to the injected script tag, used to allow-list the next-themes script in your CSP

docs/data/material/migration/migration-v5/migration-css-theme-variables.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,23 @@ The `mode` is stored inside `CssVarsProvider` which handles local storage synchr
176176
177177
## 3. Prevent dark-mode flickering in server-side applications
178178
179-
The `getInitColorSchemeScript()` API prevents dark-mode flickering by returning a script that must be run before React.
179+
The `InitColorSchemeScript` component prevents dark-mode flickering by returning a script that must be run before React.
180180
181181
### Next.js Pages Router
182182
183-
Place the script before `<Main />` in your [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document):
183+
Place the component before `<Main />` in your [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document):
184184
185185
```jsx
186186
import Document, { Html, Head, Main, NextScript } from 'next/document';
187-
import { getInitColorSchemeScript } from '@mui/material/styles';
187+
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
188188

189189
export default class MyDocument extends Document {
190190
render() {
191191
return (
192192
<Html data-color-scheme="light">
193193
<Head>...</Head>
194194
<body>
195-
{getInitColorSchemeScript()}
195+
<InitColorSchemeScript />
196196
<Main />
197197
<NextScript />
198198
</body>
@@ -208,10 +208,10 @@ Place the script in your [`gatsby-ssr.js`](https://www.gatsbyjs.com/docs/referen
208208
209209
```jsx
210210
import * as React from 'react';
211-
import { getInitColorSchemeScript } from '@mui/material/styles';
211+
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
212212

213213
export function onRenderBody({ setPreBodyComponents }) {
214-
setPreBodyComponents([getInitColorSchemeScript()]);
214+
setPreBodyComponents([<InitColorSchemeScript />]);
215215
}
216216
```
217217

docs/pages/_document.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { ServerStyleSheets as JSSServerStyleSheets } from '@mui/styles';
55
import { ServerStyleSheet } from 'styled-components';
66
import Document, { Html, Head, Main, NextScript } from 'next/document';
77
import GlobalStyles from '@mui/material/GlobalStyles';
8-
import { getInitColorSchemeScript as getMuiInitColorSchemeScript } from '@mui/material/styles';
9-
import { getInitColorSchemeScript as getJoyInitColorSchemeScript } from '@mui/joy/styles';
8+
import MuiInitColorSchemeScript from '@mui/material/InitColorSchemeScript';
9+
import JoyInitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
1010
import { pathnameToLanguage } from 'docs/src/modules/utils/helpers';
1111
import createEmotionCache from 'docs/src/createEmotionCache';
1212
import { getMetaThemeColor } from '@mui/docs/branding';
@@ -188,8 +188,8 @@ export default class MyDocument extends Document {
188188
/>
189189
</Head>
190190
<body>
191-
{getMuiInitColorSchemeScript({ defaultMode: 'system' })}
192-
{getJoyInitColorSchemeScript({ defaultMode: 'system' })}
191+
<MuiInitColorSchemeScript defaultMode="system" />
192+
<JoyInitColorSchemeScript defaultMode="system" />
193193
<Main />
194194
<script
195195
// eslint-disable-next-line react/no-danger

docs/pages/blog/first-look-at-joy.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -93,20 +93,20 @@ You're still able to override the style completely via the usual CSS overrides,
9393
Joy UI provides an effective way to prevent UI flicker when users refresh or re-enter a page with dark mode enabled.
9494
The out-of-the-box CSS variables support allows every color scheme to be rendered at build time, inserting the selected color scheme and mode before the browser renders the DOM.
9595

96-
What's more, it provides a function called `getInitColorSchemeScript()` that enables you to have perfect functioning dark mode in various React frameworks, such as Next.js, Gatsby, and Remix.
96+
What's more, it provides a component called `InitColorSchemeScript` that enables you to have perfect functioning dark mode in various React frameworks, such as Next.js, Gatsby, and Remix.
9797

9898
```js
9999
// A Next.js example
100100
import Document, { Html, Head, Main, NextScript } from 'next/document';
101-
import { getInitColorSchemeScript } from '@mui/joy/styles';
101+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
102102

103103
export default class MyDocument extends Document {
104104
render() {
105105
return (
106106
<Html data-color-scheme="light">
107107
<Head>...</Head>
108108
<body>
109-
{getInitColorSchemeScript()}
109+
<InitColorSchemeScript />
110110
<Main />
111111
<NextScript />
112112
</body>

packages/api-docs-builder-core/joyUi/projectSettings.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ export const projectSettings: ProjectSettings = {
2626
// Container's demo isn't ready
2727
// GlobalStyles's demo isn't ready
2828
return (
29-
filename.match(/(ThemeProvider|CssVarsProvider|Container|ColorInversion|GlobalStyles)/) !==
30-
null
29+
filename.match(
30+
/(ThemeProvider|CssVarsProvider|Container|ColorInversion|GlobalStyles|InitColorSchemeScript)/,
31+
) !== null
3132
);
3233
},
3334
translationPagesDirectory: 'docs/translations/api-docs-joy',

packages/api-docs-builder-core/materialUi/projectSettings.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const projectSettings: ProjectSettings = {
2828
getComponentInfo: getMaterialUiComponentInfo,
2929
translationLanguages: LANGUAGES,
3030
skipComponent(filename: string) {
31-
return filename.match(/(ThemeProvider|CssVarsProvider|Grid2)/) !== null;
31+
return filename.match(/(ThemeProvider|CssVarsProvider|InitColorSchemeScript|Grid2)/) !== null;
3232
},
3333
translationPagesDirectory: 'docs/translations/api-docs',
3434
generateClassName: generateUtilityClass,

packages/api-docs-builder-core/muiSystem/projectSettings.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export const projectSettings: ProjectSettings = {
2323
getComponentInfo: getSystemComponentInfo,
2424
translationLanguages: LANGUAGES,
2525
skipComponent(filename) {
26-
return filename.match(/(ThemeProvider|CssVarsProvider|GlobalStyles)/) !== null;
26+
return (
27+
filename.match(/(ThemeProvider|CssVarsProvider|GlobalStyles|InitColorSchemeScript)/) !== null
28+
);
2729
},
2830
translationPagesDirectory: 'docs/translations/api-docs',
2931
generateClassName: generateUtilityClass,

packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ export default async function generateComponentApi(
786786
throw new Error(
787787
'Unable to find demos. \n' +
788788
`Be sure to include \`components: ${reactApi.name}\` in the markdown pages where the \`${reactApi.name}\` component is relevant. ` +
789-
'Every public component should have a demo. ',
789+
'Every public component should have a demo.\nFor internal component, add the name of the component to the `skipComponent` method of the product.',
790790
);
791791
}
792792

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import * as React from 'react';
2+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
3+
4+
<InitColorSchemeScript nonce="foo-bar" />;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from 'react';
2+
import { expect } from 'chai';
3+
import { createRenderer } from '@mui/internal-test-utils';
4+
import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
5+
6+
describe('InitColorSchemeScript', () => {
7+
const { render } = createRenderer();
8+
9+
it('should render as expected', () => {
10+
const { container } = render(<InitColorSchemeScript />);
11+
expect(container.firstChild).to.have.tagName('script');
12+
});
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from 'react';
2+
import SystemInitColorSchemeScript from '@mui/system/InitColorSchemeScript';
3+
4+
export const defaultConfig = {
5+
attribute: 'data-joy-color-scheme',
6+
colorSchemeStorageKey: 'joy-color-scheme',
7+
defaultLightColorScheme: 'light',
8+
defaultDarkColorScheme: 'dark',
9+
modeStorageKey: 'joy-mode',
10+
} as const;
11+
12+
export default (function InitColorSchemeScript(props) {
13+
return <SystemInitColorSchemeScript {...defaultConfig} {...props} />;
14+
} as typeof SystemInitColorSchemeScript);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './InitColorSchemeScript';

0 commit comments

Comments
 (0)