Custom Theme
A theme in Nextra works like a layout, that will be rendered as a wrapper for all pages. This docs will walk you through the process of creating a custom theme.
Optionally, you can deploy an example and build further on it by following the below steps:
Create a Custom Theme
Configure Nextra to Use the Theme
First, you need to tell Nextra to use your custom theme file instead of official ones. In your Next.js config, you can pass the path to your theme file to the Nextra plugin:
import nextra from 'nextra'
const withNextra = nextra({
// ... other Nextra config options
})
// You can include other Next.js configuration options here, in addition to Nextra settings:
export default withNextra({
// ... Other Next.js config options
})
Create a Basic Theme
You can now start working on your theme! In your root directory, create the
corresponding theme.tsx
file with basic content:
export default function Layout({ children }) {
return (
<div>
<h1>My Theme</h1>
<div style={{ border: '1px solid' }}>{children}</div>
</div>
)
}
It accepts a children
prop, which is the MDX content of the current page, and
wraps some other elements around the content. After creating the theme, you can
simply add a MDX file as content/index.mdx
and see the result:
Inside your theme layout, you can use CSS imports or other ways to style it.
Next.js hooks such as useRouter
, Head
are also available.
Render Metadata for the Active Page
Other than children
, some other useful props are passed to the theme layout
too. With the pageOpts
props, the theme can access the page’s meta
information.
For example, let’s implement these features:
- Render the page title in
<title>
- Show a simple Table of Contents via MDX
<Wrapper>
component - Add a meta tag for
og:image
via the front matter
import Head from 'next/head'
import type { NextraThemeLayoutProps } from 'nextra'
import { MDXProvider } from 'nextra/mdx'
export default function Layout({ children, pageOpts }: NextraThemeLayoutProps) {
const { title, frontMatter } = pageOpts
return (
<>
<Head>
<title>{title}</title>
<meta name="og:image" content={frontMatter.image} />
</Head>
<MDXProvider components={{ wrapper: MyWrapper }}>{children}</MDXProvider>
</>
)
}
function MyWrapper({ children, toc }) {
return (
<>
<h1>My Theme</h1>
Table of Contents:
<ul>
{toc.map(heading => (
<li key={heading.value}>{heading.value}</li>
))}
</ul>
<div style={{ border: '1px solid' }}>{children}</div>
</>
)
}
Use Page Map of the Entire Site
Now, if you want to render something like a sidebar or a navigation bar, which
relies on information of not only the current page but also other pages, you can
use the pageMap
value.
For example, we can render a simple navigation list with all the pages in the top level:
import Link from 'next/link'
import type { NextraThemeLayoutProps } from 'nextra'
export default function Layout({ children, pageOpts }: NextraThemeLayoutProps) {
const { pageMap } = pageOpts
return (
<div>
<h1>My Theme</h1>
{pageMap.map(item => {
if ('route' in item && !('children' in item)) {
return (
<Link key={item.name} href={item.route}>
{item.route}
</Link>
)
}
})}
<div style={{ border: '1px solid' }}>{children}</div>
</div>
)
}
There are other item kinds such as Folder
(for directories) and Meta
(for
_meta.js
files). All the items are typed so you can easily know the
properties.
Advanced Usage
Docs for advanced usage are under construction.