自定义主题
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