在Next.js中为MDX页面配置自动默认布局的方案求助
需求与问题
我的Next.js站点同时包含MDX和TypeScript-React页面,需要实现:
- MDX页面自动应用默认布局(样式、页眉/页脚),方便非技术用户添加页面
- TypeScript-React页面不应用任何默认布局,保留完全自定义性
已尝试的方法:
- 给每个MDX文章底部添加
export default ({ children }) => <MyLayoutComponent>{children}</MyLayoutComponent>,但对非技术用户不友好 - 在
_app.tsx中全局添加布局,会影响所有页面,无法保留TS-React页面的自定义性 - 尝试beta版app文件夹,但MDX支持不完善,暂不适用
解决方案
方法1:通过_app.tsx判断页面类型(推荐)
在_app.tsx中通过路由规则或页面元数据区分MDX与TS-React页面,仅对MDX页面应用布局。
实现方式1:按路由目录判断
假设所有MDX页面存放在pages/content目录下,在_app.tsx中根据路由路径判断:
// pages/_app.tsx import type { AppProps } from 'next/app' import MyLayout from '../components/MyLayout' export default function MyApp({ Component, pageProps, router }: AppProps) { // 判断当前页面是否属于MDX专属目录 const isMDXPage = router.pathname.startsWith('/content') if (isMDXPage) { return ( <MyLayout> <Component {...pageProps} /> </MyLayout> ) } // TS-React页面直接渲染,不附加布局 return <Component {...pageProps} /> }
实现方式2:按页面元数据判断
在MDX页面顶部添加frontmatter标记,再在_app.tsx中读取该标记:
--- isMDX: true --- # 我的MDX页面内容
// pages/_app.tsx import type { AppProps } from 'next/app' import MyLayout from '../components/MyLayout' export default function MyApp({ Component, pageProps }: AppProps) { const isMDXPage = pageProps.frontmatter?.isMDX if (isMDXPage) { return ( <MyLayout> <Component {...pageProps} /> </MyLayout> ) } return <Component {...pageProps} /> }
方法2:自定义MDX Loader自动包装布局
通过修改Webpack配置,让所有MDX页面自动被布局组件包裹,无需用户手动添加导出代码。
步骤:
- 安装依赖:
npm install @next/mdx @mdx-js/loader @mdx-js/react string-replace-loader --save-dev
- 创建MDX布局包装文件:
// lib/mdx-wrapper.tsx import MyLayout from '../components/MyLayout' export default function MDXWrapper({ children }: { children: React.ReactNode }) { return <MyLayout>{children}</MyLayout> }
- 修改
next.config.js配置:
// next.config.js const withMDX = require('@next/mdx')({ extension: /\.mdx?$/, options: { remarkPlugins: [], rehypePlugins: [], providerImportSource: '@mdx-js/react', }, }) module.exports = withMDX({ pageExtensions: ['ts', 'tsx', 'md', 'mdx'], webpack: (config) => { // 找到MDX对应的Loader const mdxLoader = config.module.rules.find(rule => rule.test?.test('.mdx')) if (mdxLoader) { // 注入代码,自动用布局包裹MDX内容 mdxLoader.use.push({ loader: 'string-replace-loader', options: { search: 'export default function MDXContent(props) {', replace: `import MDXWrapper from '../lib/mdx-wrapper'; export default function MDXContent(props) { return <MDXWrapper><MDXContentInner {...props} /></MDXWrapper>; } function MDXContentInner(props) {`, }, }) } return config }, })
此配置会自动修改所有MDX页面的默认导出逻辑,非技术用户只需编写MDX内容即可。
方法3:使用next-mdx-remote统一渲染MDX页面
将MDX内容作为静态资源存储,通过单个TSX页面统一读取并渲染,自动应用布局,TS-React页面保持独立。
步骤:
- 安装依赖:
npm install next-mdx-remote @mdx-js/react
- 创建动态路由页面
pages/[slug].tsx:
// pages/[slug].tsx import { GetStaticProps, GetStaticPaths } from 'next' import { MDXRemote } from 'next-mdx-remote' import { serialize } from 'next-mdx-remote/serialize' import MyLayout from '../components/MyLayout' import fs from 'fs' import path from 'path' // MDX文件存储目录 const MDX_DIR = path.join(process.cwd(), 'content') export default function MDXPage({ source }: { source: any }) { return ( <MyLayout> <MDXRemote {...source} /> </MyLayout> ) } // 生成所有MDX页面的路由 export const getStaticPaths: GetStaticPaths = async () => { const files = fs.readdirSync(MDX_DIR) const paths = files.map(file => ({ params: { slug: file.replace('.mdx', '') }, })) return { paths, fallback: false } } // 获取MDX内容并序列化 export const getStaticProps: GetStaticProps = async ({ params }) => { const slug = params?.slug as string const content = fs.readFileSync(path.join(MDX_DIR, `${slug}.mdx`), 'utf-8') const source = await serialize(content) return { props: { source } } }
非技术用户只需在content目录下添加MDX文件,访问/[文件名]即可自动应用布局渲染,其他TS-React页面直接在pages目录下创建即可。
内容的提问来源于stack exchange,提问作者domdomegg
相关产品推荐
相关产品推荐

