Skip to Content
🎉 Nextra 4.0 已发布。Dima Machina 正在 寻找新的工作或咨询机会
Documentation自定义主题

自定义主题

Nextra 中的主题类似于布局,它将作为所有页面的包装器进行渲染。本文档将指导您创建自定义主题的过程。

Note

以下自定义主题的源代码可以在 这里找到。

创建自定义主题

创建根布局

app/layout.tsx
import type { Metadata } from 'next' import { Head } from 'nextra/components' import { getPageMap } from 'nextra/page-map' import type { FC, ReactNode } from 'react' import { NextraTheme } from './_components/nextra-theme' export const metadata: Metadata = { title: { absolute: '', template: '%s - Nextra' } } const RootLayout: FC<{ children: ReactNode }> = async ({ children }) => { const pageMap = await getPageMap() return ( <html lang="en" dir="ltr"> <Head faviconGlyph="✦" /> <body style={{ margin: 0 }}> <NextraTheme pageMap={pageMap}>{children}</NextraTheme> </body> </html> ) } export default RootLayout

创建 mdx-components 文件

mdx-components.jsx
import { useMDXComponents as getNextraComponents } from 'nextra/mdx-components' import { TOC } from './app/_components/toc' const defaultComponents = getNextraComponents({ wrapper({ children, toc }) { return ( <> <div style={{ flexGrow: 1, padding: 20 }}>{children}</div> <TOC toc={toc} /> </> ) } }) export const useMDXComponents = components => ({ ...defaultComponents, ...components })
app/_components/toc.tsx
import type { Heading } from 'nextra' import type { FC } from 'react' export const TOC: FC<{ toc: Heading[] }> = ({ toc }) => { return ( <div style={{ background: 'lightblue', padding: 20 }}> <h3>Table of Contents</h3> <ul> {toc.map(heading => ( <li key={heading.id}>{heading.value}</li> ))} </ul> </div> ) }

创建基础主题

现在您可以开始开发主题了!创建 nextra-theme.tsx 文件,它接受一个 children 属性, 这是当前页面的 MDX 内容,并在内容周围包装其他元素:

app/_components/nextra-theme.tsx
import type { PageMapItem } from 'nextra' import { version } from 'nextra/package.json' import type { FC, ReactNode } from 'react' import { Footer } from './footer' import { Navbar } from './navbar' import { Sidebar } from './sidebar' export const NextraTheme: FC<{ children: ReactNode pageMap: PageMapItem[] }> = ({ children, pageMap }) => { return ( <> <h1 style={{ margin: 0, padding: 20, background: 'lightslategray', fontWeight: 'normal' }} > Custom theme demo for <strong>Nextra {version}</strong> </h1> <Navbar pageMap={pageMap} /> <div style={{ display: 'flex' }}> <Sidebar pageMap={pageMap} /> {children} </div> <Footer /> </> ) }

创建导航栏和页脚

app/_components/footer.tsx
import type { FC } from 'react' export const Footer: FC = () => { return ( <footer style={{ background: 'lightsalmon', padding: 20 }}> Powered by Nextra {new Date().getFullYear()} </footer> ) }
app/_components/navbar.tsx
'use client' import { usePathname } from 'next/navigation' import type { PageMapItem } from 'nextra' import { Anchor } from 'nextra/components' import { normalizePages } from 'nextra/normalize-pages' import type { FC } from 'react' export const Navbar: FC<{ pageMap: PageMapItem[] }> = ({ pageMap }) => { const pathname = usePathname() const { topLevelNavbarItems } = normalizePages({ list: pageMap, route: pathname }) return ( <ul style={{ display: 'flex', listStyleType: 'none', padding: 20, gap: 20, background: 'lightcoral', margin: 0 }} > {topLevelNavbarItems.map(item => { const route = item.route || ('href' in item ? item.href! : '') return ( <li key={route}> <Anchor href={route} style={{ textDecoration: 'none' }}> {item.title} </Anchor> </li> ) })} </ul> ) }

创建侧边栏

app/_components/sidebar.tsx
'use client' import { usePathname } from 'next/navigation' import type { PageMapItem } from 'nextra' import { Anchor } from 'nextra/components' import { normalizePages } from 'nextra/normalize-pages' import type { FC } from 'react' export const Sidebar: FC<{ pageMap: PageMapItem[] }> = ({ pageMap }) => { const pathname = usePathname() const { docsDirectories } = normalizePages({ list: pageMap, route: pathname }) return ( <div style={{ background: 'lightgreen', padding: 20 }} > <h3>Sidebar</h3> <ul style={{ margin: 0, display: 'flex', flexDirection: 'column', listStyleType: 'none', padding: 0, gap: 20 }} > {docsDirectories.map(function renderItem(item) { const route = item.route || ('href' in item ? (item.href as string) : '') const { title } = item return ( <li key={route} style={{ padding: '4px 4px 4px 10px', border: '1px solid' }} > {'children' in item ? ( <details> <summary>{title}</summary> {item.children.map(child => renderItem(child))} </details> ) : ( <Anchor href={route} style={{ textDecoration: 'none' }}> {title} </Anchor> )} </li> ) })} </ul> </div> ) }

添加第一个 MDX 页面

创建主题后,您可以简单地添加一个 MDX 文件作为 app/page.mdx 并查看结果:

自定义主题


在您的主题布局中,您可以使用 CSS 导入或其他方式来设置样式。 Next.js 钩子(如 usePathname)也是可用的。

Last updated on