Next.js App Router
了解如何在 Next.js App Router 中使用 MUI Base。
範例
在新 App Router 架構的專案中從頭開始?
透過這個範例:MUI Base - 搭配 TypeScript 和 Tailwind CSS 的 Next.js App Router,直接進入程式碼。
Next.js 和 React 伺服器組件
Next.js App Router 實作了 React 伺服器組件,React 即將推出的功能。
為了支援 App Router,MUI Base 中需要存取瀏覽器 API 的組件和 Hook 都使用 "use client"
指令匯出。
在 App Router 中設定 MUI Base
MUI Base 讓您可以自由選擇自己的樣式解決方案,因此設定 Next.js App Router 專案很大程度上取決於您的選擇。本指南涵蓋 Tailwind CSS、Emotion 和其他 CSS-in-JS 解決方案,例如 styled-components。
Tailwind CSS
依照Tailwind CSS 關於搭配 Next.js 使用的指南,並務必將 app
目錄和其他目錄新增至 tailwind.config.js
,如下所示
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}'
// or if not using the `src` directory:
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};
請參考這個範例儲存庫,以取得使用 MUI Base 和 Tailwind CSS 的 Next.js 13 應用程式完整運作示範。
Emotion
如果您使用 Emotion,或類似 MUI System 等基於 Emotion 的套件,請建立自訂的 ThemeRegistry
組件,將 Emotion CacheProvider
、Material UI ThemeProvider
和 next/navigation
中的 useServerInsertedHTML
Hook 結合在一起,如下所示
// app/ThemeRegistry.tsx
'use client';
import createCache from '@emotion/cache';
import { useServerInsertedHTML } from 'next/navigation';
import { CacheProvider, ThemeProvider } from '@emotion/react';
import theme from '/path/to/your/theme';
// This implementation is from emotion-js
// https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902
export default function ThemeRegistry(props) {
const { options, children } = props;
const [{ cache, flush }] = React.useState(() => {
const cache = createCache(options);
cache.compat = true;
const prevInsert = cache.insert;
let inserted: string[] = [];
cache.insert = (...args) => {
const serialized = args[1];
if (cache.inserted[serialized.name] === undefined) {
inserted.push(serialized.name);
}
return prevInsert(...args);
};
const flush = () => {
const prevInserted = inserted;
inserted = [];
return prevInserted;
};
return { cache, flush };
});
useServerInsertedHTML(() => {
const names = flush();
if (names.length === 0) {
return null;
}
let styles = '';
for (const name of names) {
styles += cache.inserted[name];
}
return (
<style
key={cache.key}
data-emotion={`${cache.key} ${names.join(' ')}`}
dangerouslySetInnerHTML={{
__html: styles,
}}
/>
);
});
return (
<CacheProvider value={cache}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</CacheProvider>
);
}
// app/layout.js
export default function RootLayout(props) {
return (
<html lang="en">
<body>
<ThemeRegistry options={{ key: 'mui' }}>{props.children}</ThemeRegistry>
</body>
</html>
);
}
如果您需要進一步覆寫主題樣式(例如使用 CSS Modules),Emotion 為 createCache
提供 prepend: true
選項來反轉注入順序,以便自訂樣式可以覆寫主題,而無需使用 !important
。
目前,prepend
在 App Router 中無法可靠地運作,但您可以透過將 Emotion 樣式包裝在 CSS @layer
中並修改上述程式碼片段來解決此問題
useServerInsertedHTML(() => {
const names = flush();
if (names.length === 0) {
return null;
}
let styles = '';
for (const name of names) {
styles += cache.inserted[name];
}
return (
<style
key={cache.key}
data-emotion={`${cache.key} ${names.join(' ')}`}
dangerouslySetInnerHTML={{
- __html: styles,
+ __html: options.prepend ? `@layer emotion {${styles}}` : styles,
}}
/>
);
});
其他 CSS-in-JS 函式庫
若要將 Next.js 與 MUI Base 和 styled-components 或其他 CSS-in-JS 解決方案搭配使用,請依照 Next.js 關於 CSS-in-JS 的文件。
自訂
為插槽屬性使用回呼函數
MUI Base 中常見的自訂方法是將回呼函數傳遞給 slotProps
中的插槽,以便套用動態屬性。例如,您可能想要在按鈕停用時,透過套用不同的類別來變更背景顏色
// page.tsx
export default function Page() {
return (
<React.Fragment>
{/* Next.js won't render this button without 'use-client'*/}
<Button
slotProps={{
root: (ownerState: ButtonOwnerState) => ({
className: ownerState.disabled ? 'bg-gray-400' : 'bg-blue-400',
}),
}}
>
Submit
</Button>
{/* Next.js can render this */}
<Button
slotProps={{
root: {
className: 'bg-gray-400',
},
}}
>
Return
</Button>
</React.Fragment>
);
}
遺憾的是,由於函數屬性是不可序列化的,這在伺服器組件中無法運作。相反地,Next.js 團隊建議將這類組件「向下移動到樹狀結構中」,以避免此問題並提升整體效能。