Intro
Hi everyone ๐, I am Dimitri POSTOLOV, you may know me previously as B2o5T (boost).
I recently decided to change my nickname to easy-to-remember Dima Machina (for a long time thatโs what my team The Guild called me).
Once upon a timeโฆ I discovered Nextra and wanted to use it as a base framework for The Guild documentation websites, I found a lot of issues and started to contribute to this library to fix them and to help with the v2 release.

Nextra 3 is currently available as an alpha release, and I am looking for feedback from the community, choose your favourite package manager and try it:
npm i nextra@alpha # Install Nextra
npm i nextra-theme-docs@alpha # Install Nextra Theme Docs
npm i nextra-theme-blog@alpha # Install Nextra Theme Blog
Whatโs New
Starting from releasing v2 in which release I actively contributed, I actively maintained Nextra and received feedback from the community and ideas for future improvements.
So letโs begin what I prepared with a new major release ๐.
MDX 3
Nextra 3 will be built on top of MDX 3. There are no significant breaking changes (as it was between MDX 1 and MDX 2). More info about whatโs new in MDX 3 can be found in the official blog post.
Complete New I18n with Static Exports Support
I18n was rewritten from scratch. The previous i18n was hard to scalable especially when you have a lot of languages, but also had really blocking issues, especially:
-
only the default locale was produced in sitemap #712
-
index files were not working for subfolders #1263
-
build output directory for each locale contained all pages locales #1922
-
some static files were inaccessible #1354
The original proposal of better i18n by locale folders instead of locale suffix was proposed 2 years ago and in Nextra 3 it was implemented by me.
Comparison of I18n Project Structure for Nextra 2/3
pages/
โโโ index.en.mdx
โโโ index.fr.mdx
โโโ index.ru.mdx
โโโ getting-started.en.mdx
โโโ getting-started.fr.mdx
โโโ getting-started.ru.mdx
โโโ _meta.en.json
โโโ _meta.fr.json
โโโ _meta.ru.json
pages/
โโโ en/
โ โโโ index.mdx
โ โโโ getting-started.mdx
โ โโโ _meta.js
โโโ fr/
โ โโโ index.mdx
โ โโโ getting-started.mdx
โ โโโ _meta.js
โโโ ru/
โโโ index.mdx
โโโ getting-started.mdx
โโโ _meta.js
You should know also a few notes:
- By default, Next.js doesnโt support project structure like above with the
i18nproperty enabled in yournext.config
If without Nextra youโll try to create this project structure, and you try to access
http://localhost:3000/en or http://localhost:3000/en/getting-started youโll get a 404 error. To fix
it Nextra set the i18n property in next.config to undefined. With i18n enabled and with
Nextra youโll see the following console warning:
- warn [nextra] Next.js doesn't support i18n by locale folder names.
When i18n enabled, Nextra unset nextConfig.i18n to `undefined`, use `useRouter` from `nextra/hooks`
if you need `locale` or `defaultLocale` values.
-
useRouterfromnext/routerreturns incorrectlocaleanddefaultLocalevalues (sincei18nwas unset), so instead you should useuseRouterfromnextra/hookswhich will return correct values. -
Popular i18n libraries like
react-intlandreact-i18nextor others will not work either.
New _meta.{js,jsx,ts,tsx} Files with JSX Support
After one year of
creating my proposal of using _meta.js instead of _meta.json,
I finally found a way of implementing the trickiest feature of Nextra 3 and _meta.json files are
no longer supported.
This means that:
- you can use either
_meta.jsor_meta.jsxwith JSX support or_meta.{ts,tsx}for TypeScript projects instead - you can use ESLintโs built-in rule
sort-keyswith/* eslint sort-keys: error */comment to sort your sidebar items alphabetically - you can export and import everything that you want in your
_metafiles - you can have typesafe
_meta.{ts,tsx}files while asserting type definition from thenextrapackage - you can add a banner/footer for your repeated content in
_metafiles, for individual pages, or for all nested pages in*key
More Powerful TOC
There is no MDX framework where TOC can be better than in Nextra 3 (Correct me if I am wrong ๐).
Previously TOC could show only static content from your headings, and it showed unexpected results for dynamic content #2411 #1955 #2076 #1884.
With Nextra 3 all issues are fixed, and everything that is inserted in your main content is properly rendered in TOC too ๐.
Better Bundle Size
A lot of work was done to make Nextraโs bundle size smaller, I deep-dived into the generated _app
file and took a look what is included and what can be removed.
Letโs do some calculations! ๐งฎ
Build Output of Blog Example Website
with Nextra 2
https://github.com/shuding/nextra/tree/66798f8e7f92cca80f2d62d19f9db5667bcc62ef/examples/blog
Route (pages) Size First Load JS
โ โ / 1.08 kB 119 kB
โ /_app 0 B 114 kB
โ โ /404 180 B 115 kB
โ โ /posts 588 B 119 kB
โ โ /posts/aaron-swartz-a-programmable-web 3.53 kB 122 kB
โ โ /posts/callout 1.98 kB 120 kB
โ โ /posts/code-blocks 1.53 kB 120 kB
โ โ /posts/draft 795 B 119 kB
โ โ /posts/table 844 B 119 kB
โ โ /tags/[tag] (716 ms) 788 B 119 kB
โ /tags/web development
โ /tags/JavaScript
โ /tags/GraphQL
โ [+6 more paths]
+ First Load JS shared by all 123 kB
โ chunks/framework-bce0fd2bcc8d4c85.js 45.3 kB
โ chunks/main-48077aa82984b023.js 37.6 kB
โ chunks/pages/_app-83deac736929a1bc.js 29.6 kB
โ chunks/webpack-109eb0aced926db3.js 1.81 kB
โ css/e187e49945a86f29.css 8.61 kB
โ (Static) automatically rendered as static HTML (uses no initial props)
โ (SSG) automatically generated as static HTML + JSON (uses getStaticProps)with Nextra 3
https://github.com/shuding/nextra/tree/c2ad837d3dacd87e1aa3ea3b9839f18062ae1c25/examples/blog
Route (pages) Size First Load JS
โ โ / 1.61 kB 108 kB
โ โ /404 180 B 101 kB
โ โ /posts 1.12 kB 107 kB
โ โ /posts/aaron-swartz-a-programmable-web 3.93 kB 110 kB
โ โ /posts/callout 11.2 kB 117 kB
โ โ /posts/code-blocks 2.02 kB 108 kB
โ โ /posts/draft 1.28 kB 107 kB
โ โ /posts/table 1.35 kB 107 kB
โ โ /tags/[tag] (872 ms) 1.34 kB 107 kB
โ /tags/web development
โ /tags/JavaScript
โ /tags/GraphQL
โ [+6 more paths]
+ First Load JS shared by all 101 kB
โ chunks/framework-bce0fd2bcc8d4c85.js 45.3 kB
โ chunks/main-c12ab09220a28f5f.js 37.8 kB
โ chunks/pages/_app-f6bbbcf4ee8f890e.js 16.2 kB
โ chunks/webpack-d4712bf1c9b5b172.js 1.81 kB
โ (Static) automatically rendered as static HTML (uses no initial props)
โ (SSG) automatically generated as static HTML + JSON (uses getStaticProps)Letโs compare everything starting from the First Load JS shared by all lines:
-+ First Load JS shared by all 123 kB
++ First Load JS shared by all 101 kB
โ chunks/framework-bce0fd2bcc8d4c85.js 45.3 kB
- โ chunks/main-48077aa82984b023.js 37.6 kB
+ โ chunks/main-c12ab09220a28f5f.js 37.8 kB
- โ chunks/pages/_app-83deac736929a1bc.js 29.6 kB
+ โ chunks/pages/_app-f6bbbcf4ee8f890e.js 16.2 kB
โ chunks/webpack-109eb0aced926db3.js 1.81 kB
- โ css/e187e49945a86f29.css 8.61 kB
Build Output of I18n Docs Website with LaTeX Enabled
with Nextra 2
https://github.com/shuding/nextra/tree/66798f8e7f92cca80f2d62d19f9db5667bcc62ef/examples/swr-site
Route (pages) Size First Load JS
โ /_app 0 B 187 kB
โ โ /404 560 B 191 kB
โ โ /500 555 B 191 kB
โ โ /about/a-page.en-US 527 B 191 kB
โ โ /about/acknowledgement.en-US 526 B 191 kB
โ โ /about/changelog.en-US 1.22 kB 192 kB
โ โ /about/team.en-US 574 B 191 kB
โ โ /blog.en-US 1.5 kB 192 kB
โ โ /blog.ru 1.5 kB 192 kB
โ โ /blog/swr-v1.en-US 6.32 kB 197 kB
โ โ /blog/swr-v1.ru 8.1 kB 199 kB
โ โ /docs/404-500.en-US 1.44 kB 192 kB
โ โ /docs/advanced.en-US 909 B 192 kB
โ โ /docs/advanced/cache.en-US 4.46 kB 204 kB
โ โ /docs/advanced/cache.ru 5.74 kB 205 kB
โ โ /docs/advanced/code-highlighting.en-US 1.39 kB 192 kB
โ โ /docs/advanced/dynamic-markdown-import.en-US 2.92 kB 194 kB
โ โ /docs/advanced/file-name.with.DOTS.en-US 557 B 191 kB
โ โ /docs/advanced/file-name.with.DOTS.es-ES 568 B 191 kB
โ โ /docs/advanced/file-name.with.DOTS.ru 621 B 191 kB
โ โ /docs/advanced/images.en-US 892 B 192 kB
โ โ /docs/advanced/markdown-import.en-US 4.69 kB 195 kB
โ โ /docs/advanced/more/loooooooooooooooooooong-title.en-US 637 B 191 kB
โ โ /docs/advanced/more/tree/one.en-US 588 B 191 kB
โ โ /docs/advanced/more/tree/three.en-US 532 B 191 kB
โ โ /docs/advanced/more/tree/two.en-US 587 B 191 kB
โ โ /docs/advanced/performance.en-US 2.97 kB 194 kB
โ โ /docs/advanced/performance.es-ES 3.15 kB 194 kB
โ โ /docs/advanced/performance.ru 3.79 kB 195 kB
โ โ /docs/advanced/react-native.en-US 2.51 kB 193 kB
โ โ /docs/advanced/react-native.ru 3.08 kB 194 kB
โ โ /docs/advanced/scrollbar-x.en-US 1.72 kB 192 kB
โ โ /docs/arguments.en-US 1.9 kB 193 kB
โ โ /docs/arguments.es-ES 1.98 kB 193 kB
โ โ /docs/arguments.ru 2.23 kB 193 kB
โ โ /docs/callout.en-US 693 B 191 kB
โ โ /docs/change-log.en-US 898 B 192 kB
โ โ /docs/change-log.es-ES 928 B 197 kB
โ โ /docs/change-log.ru 1.04 kB 197 kB
โ โ /docs/code-block-without-language.en-US 718 B 191 kB
โ โ /docs/conditional-fetching.en-US 1.56 kB 192 kB
โ โ /docs/conditional-fetching.es-ES 1.62 kB 192 kB
โ โ /docs/conditional-fetching.ru 1.87 kB 193 kB
โ โ /docs/custom-header-ids.en-US 1.02 kB 192 kB
โ โ /docs/data-fetching.en-US 1.97 kB 193 kB
โ โ /docs/data-fetching.es-ES 2.02 kB 193 kB
โ โ /docs/data-fetching.ru 2.33 kB 193 kB
โ โ /docs/error-handling.en-US 2.81 kB 194 kB
โ โ /docs/error-handling.es-ES 2.96 kB 194 kB
โ โ /docs/error-handling.ru 3.55 kB 194 kB
โ โ /docs/getting-started.en-US 8.96 kB 200 kB
โ โ /docs/getting-started.es-ES 7.88 kB 199 kB
โ โ /docs/getting-started.ru 8.58 kB 199 kB
โ โ /docs/global-configuration.en-US 1.85 kB 193 kB
โ โ /docs/global-configuration.es-ES 1.9 kB 193 kB
โ โ /docs/global-configuration.ru 2.22 kB 193 kB
โ โ /docs/middleware.en-US 3.9 kB 195 kB
โ โ /docs/middleware.ru 4.75 kB 196 kB
โ โ /docs/mutation.en-US 3.41 kB 194 kB
โ โ /docs/mutation.es-ES 3.48 kB 194 kB
โ โ /docs/mutation.ru 4.21 kB 195 kB
โ โ /docs/options.en-US 2.42 kB 193 kB
โ โ /docs/options.es-ES 2.57 kB 193 kB
โ โ /docs/options.ru 3.21 kB 194 kB
โ โ /docs/pagination.en-US 6.02 kB 204 kB
โ โ /docs/pagination.es-ES 6.42 kB 204 kB
โ โ /docs/pagination.ru 7.38 kB 205 kB
โ โ /docs/prefetching.en-US 2.05 kB 193 kB
โ โ /docs/prefetching.es-ES 2.14 kB 193 kB
โ โ /docs/prefetching.ru 2.56 kB 193 kB
โ โ /docs/raw-layout.en-US 1.24 kB 192 kB
โ โ /docs/revalidation.en-US 2.76 kB 198 kB
โ โ /docs/revalidation.es-ES 2.9 kB 198 kB
โ โ /docs/revalidation.ru 3.5 kB 199 kB
โ โ /docs/suspense.en-US 2.08 kB 193 kB
โ โ /docs/suspense.es-ES 2.15 kB 193 kB
โ โ /docs/suspense.ru 2.53 kB 193 kB
โ โ /docs/typescript.en-US 2.37 kB 193 kB
โ โ /docs/understanding.en-US 3.88 kB 199 kB
โ โ /docs/understanding.es-ES 3.88 kB 199 kB
โ โ /docs/understanding.ru 3.88 kB 199 kB
โ โ /docs/with-nextjs.en-US 2.25 kB 193 kB
โ โ /docs/with-nextjs.es-ES 2.39 kB 193 kB
โ โ /docs/with-nextjs.ru 2.82 kB 194 kB
โ โ /docs/wrap-toc-items.en-US 1.39 kB 192 kB
โ โ /docs/wrap-toc-items.es-ES 1.39 kB 192 kB
โ โ /docs/wrap-toc-items.ru 1.39 kB 192 kB
โ โ /examples/auth.en-US 830 B 192 kB
โ โ /examples/auth.es-ES 834 B 192 kB
โ โ /examples/auth.ru 865 B 192 kB
โ โ /examples/basic.en-US 832 B 192 kB
โ โ /examples/basic.es-ES 834 B 192 kB
โ โ /examples/basic.ru 875 B 192 kB
โ โ /examples/error-handling.en-US 839 B 192 kB
โ โ /examples/error-handling.es-ES 842 B 192 kB
โ โ /examples/error-handling.ru 875 B 192 kB
โ โ /examples/full.en-US 815 B 192 kB
โ โ /examples/infinite-loading.en-US 851 B 192 kB
โ โ /examples/infinite-loading.es-ES 853 B 192 kB
โ โ /examples/infinite-loading.ru 896 B 192 kB
โ โ /examples/ssr.en-US 840 B 192 kB
โ โ /examples/ssr.ru 838 B 192 kB
โ โ /foo.en-US 954 B 192 kB
โ โ /index.en-US 4.34 kB 195 kB
โ โ /index.es-ES 3.99 kB 195 kB
โ โ /index.ru 4.38 kB 195 kB
โ โ /remote/_meta 225 B 187 kB
โ โ /remote/graphql-eslint/_meta 237 B 187 kB
โ โ /remote/graphql-yoga/_meta 236 B 187 kB
โ โ /test.en-US 786 B 192 kB
+ First Load JS shared by all 203 kB
โ chunks/framework-733009b6474116fd.js 45.3 kB
โ chunks/main-1dbce7e00d8751d5.js 38.4 kB
โ chunks/pages/_app-f3b27ac2556aba2f.js 101 kB
โ chunks/webpack-e7e126cbd02a5d11.js 1.95 kB
โ css/86d65edbc5de703f.css 16.1 kB
ฦ Middleware 26.8 kB
โ (Static) automatically rendered as static HTML (uses no initial props)
โ (SSG) automatically generated as static HTML + JSON (uses getStaticProps)with Nextra 3
https://github.com/shuding/nextra/tree/c2ad837d3dacd87e1aa3ea3b9839f18062ae1c25/examples/swr-site
Route (pages) Size First Load JS
โ /_app 0 B 148 kB
โ โ /404 183 B 149 kB
โ โ /en 8.34 kB 164 kB
โ โ /en/_meta 247 B 149 kB
โ โ /en/about/_meta 251 B 149 kB
โ โ /en/about/a-page 472 B 156 kB
โ โ /en/about/acknowledgement 471 B 156 kB
โ โ /en/about/changelog 1.17 kB 157 kB
โ โ /en/about/team 518 B 156 kB
โ โ /en/blog 1.22 kB 157 kB
โ โ /en/blog/_meta 250 B 149 kB
โ โ /en/blog/swr-v1 6.18 kB 162 kB
โ โ /en/docs/_meta 250 B 149 kB
โ โ /en/docs/404-500 1.28 kB 157 kB
โ โ /en/docs/advanced 855 B 157 kB
โ โ /en/docs/advanced/_meta 258 B 149 kB
โ โ /en/docs/advanced/cache 4.46 kB 170 kB
โ โ /en/docs/advanced/code-highlighting 1.38 kB 157 kB
โ โ /en/docs/advanced/dynamic-markdown-import 2.89 kB 159 kB
โ โ /en/docs/advanced/file-name.with.DOTS 502 B 156 kB
โ โ /en/docs/advanced/images 829 B 157 kB
โ โ /en/docs/advanced/markdown-import 8.66 kB 165 kB
โ โ /en/docs/advanced/more/loooooooooooooooooooong-title 591 B 157 kB
โ โ /en/docs/advanced/more/tree/one 507 B 156 kB
โ โ /en/docs/advanced/more/tree/three 475 B 156 kB
โ โ /en/docs/advanced/more/tree/two 492 B 156 kB
โ โ /en/docs/advanced/performance 2.95 kB 159 kB
โ โ /en/docs/advanced/react-native 3.02 kB 159 kB
โ โ /en/docs/advanced/scrollbar-x 5.33 kB 161 kB
โ โ /en/docs/arguments 2.35 kB 158 kB
โ โ /en/docs/callout 1.13 kB 157 kB
โ โ /en/docs/change-log 810 B 157 kB
โ โ /en/docs/code-block-without-language 604 B 157 kB
โ โ /en/docs/conditional-fetching 1.54 kB 157 kB
โ โ /en/docs/custom-header-ids 987 B 157 kB
โ โ /en/docs/data-fetching 2.45 kB 158 kB
โ โ /en/docs/error-handling 3.3 kB 159 kB
โ โ /en/docs/getting-started 14.4 kB 170 kB
โ โ /en/docs/global-configuration 1.86 kB 158 kB
โ โ /en/docs/middleware 4.28 kB 160 kB
โ โ /en/docs/mutation 3.36 kB 159 kB
โ โ /en/docs/options 2.86 kB 159 kB
โ โ /en/docs/pagination 5.9 kB 170 kB
โ โ /en/docs/prefetching 2.02 kB 158 kB
โ โ /en/docs/raw-layout 1.2 kB 157 kB
โ โ /en/docs/revalidation 2.71 kB 164 kB
โ โ /en/docs/suspense 2.52 kB 158 kB
โ โ /en/docs/typescript 2.36 kB 158 kB
โ โ /en/docs/understanding 3.85 kB 165 kB
โ โ /en/docs/with-nextjs 2.71 kB 159 kB
โ โ /en/docs/wrap-toc-items 1.31 kB 157 kB
โ โ /en/examples/_meta 254 B 149 kB
โ โ /en/examples/auth 738 B 157 kB
โ โ /en/examples/basic 738 B 157 kB
โ โ /en/examples/error-handling 747 B 157 kB
โ โ /en/examples/full 729 B 157 kB
โ โ /en/examples/infinite-loading 753 B 157 kB
โ โ /en/examples/ssr 754 B 157 kB
โ โ /en/foo 4.68 kB 161 kB
โ โ /en/remote/graphql-eslint/_meta 264 B 149 kB
โ โ /en/remote/graphql-eslint/[[...slug]] (1853 ms) 4.86 kB 161 kB
โ โ /en/remote/graphql-eslint/custom-rules (464 ms)
โ โ /en/remote/graphql-eslint/getting-started (373 ms)
โ โ /en/remote/graphql-eslint/getting-started/parser-options (345 ms)
โ โ /en/remote/graphql-eslint/configs
โ โ /en/remote/graphql-eslint/getting-started/parser
โ โ /en/remote/graphql-eslint/index
โ โ /en/remote/graphql-yoga/_meta 263 B 149 kB
โ โ /en/remote/graphql-yoga/[[...slug]] (32819 ms) 4.88 kB 161 kB
โ โ /en/remote/graphql-yoga/features/subscriptions (2996 ms)
โ โ /en/remote/graphql-yoga/features/context (2885 ms)
โ โ /en/remote/graphql-yoga/features/file-uploads (2777 ms)
โ โ /en/remote/graphql-yoga/features/apollo-federation (2738 ms)
โ โ /en/remote/graphql-yoga/features/graphiql (2724 ms)
โ โ /en/remote/graphql-yoga/features/cors (2674 ms)
โ โ /en/remote/graphql-yoga/features/testing (2560 ms)
โ โ [+16 more paths] (avg 842 ms)
โ โ /en/test 699 B 157 kB
โ โ /es 5.56 kB 159 kB
โ โ /es/_meta 247 B 149 kB
โ โ /es/docs/_meta 251 B 149 kB
โ โ /es/docs/advanced/_meta 256 B 149 kB
โ โ /es/docs/advanced/file-name.with.DOTS 1.78 kB 155 kB
โ โ /es/docs/advanced/performance 4.39 kB 157 kB
โ โ /es/docs/arguments 3.68 kB 157 kB
โ โ /es/docs/change-log (1096 ms) 2.13 kB 161 kB
โ โ /es/docs/conditional-fetching 2.86 kB 156 kB
โ โ /es/docs/data-fetching 3.73 kB 157 kB
โ โ /es/docs/error-handling 4.69 kB 158 kB
โ โ /es/docs/getting-started 9.69 kB 163 kB
โ โ /es/docs/global-configuration 3.18 kB 156 kB
โ โ /es/docs/mutation 4.72 kB 158 kB
โ โ /es/docs/options 4.21 kB 157 kB
โ โ /es/docs/pagination 7.57 kB 168 kB
โ โ /es/docs/prefetching 3.34 kB 156 kB
โ โ /es/docs/revalidation 4.09 kB 162 kB
โ โ /es/docs/suspense 3.85 kB 157 kB
โ โ /es/docs/understanding 5.1 kB 163 kB
โ โ /es/docs/with-nextjs 4.07 kB 157 kB
โ โ /es/docs/wrap-toc-items 2.6 kB 156 kB
โ โ /es/examples/_meta 253 B 149 kB
โ โ /es/examples/auth 1.99 kB 155 kB
โ โ /es/examples/basic 2 kB 155 kB
โ โ /es/examples/error-handling 2 kB 155 kB
โ โ /es/examples/infinite-loading 2 kB 155 kB
โ โ /ru 6.18 kB 159 kB
โ โ /ru/_meta 248 B 149 kB
โ โ /ru/blog 2.85 kB 156 kB
โ โ /ru/blog/_meta 251 B 149 kB
โ โ /ru/blog/swr-v1 9.12 kB 162 kB
โ โ /ru/docs/_meta 250 B 149 kB
โ โ /ru/docs/advanced/_meta 258 B 149 kB
โ โ /ru/docs/advanced/cache 7.15 kB 169 kB
โ โ /ru/docs/advanced/file-name.with.DOTS 2.15 kB 155 kB
โ โ /ru/docs/advanced/performance 5.2 kB 158 kB
โ โ /ru/docs/advanced/react-native 5.05 kB 158 kB
โ โ /ru/docs/arguments 4.21 kB 157 kB
โ โ /ru/docs/change-log (1101 ms) 2.56 kB 161 kB
โ โ /ru/docs/conditional-fetching 3.36 kB 156 kB
โ โ /ru/docs/data-fetching 4.26 kB 157 kB
โ โ /ru/docs/error-handling 5.49 kB 159 kB
โ โ /ru/docs/getting-started 10.6 kB 164 kB
โ โ /ru/docs/global-configuration 3.73 kB 157 kB
โ โ /ru/docs/middleware 6.59 kB 160 kB
โ โ /ru/docs/mutation 5.65 kB 159 kB
โ โ /ru/docs/options 5.05 kB 158 kB
โ โ /ru/docs/pagination 8.66 kB 169 kB
โ โ /ru/docs/prefetching 3.97 kB 157 kB
โ โ /ru/docs/revalidation 4.91 kB 163 kB
โ โ /ru/docs/suspense 4.47 kB 158 kB
โ โ /ru/docs/understanding 5.5 kB 164 kB
โ โ /ru/docs/with-nextjs 4.73 kB 158 kB
โ โ /ru/docs/wrap-toc-items 2.98 kB 156 kB
โ โ /ru/examples/_meta 253 B 149 kB
โ โ /ru/examples/auth 2.37 kB 155 kB
โ โ /ru/examples/basic 2.37 kB 155 kB
โ โ /ru/examples/error-handling 2.38 kB 155 kB
โ โ /ru/examples/infinite-loading 2.38 kB 155 kB
โ โ /ru/examples/ssr 2.38 kB 155 kB
+ First Load JS shared by all 163 kB
โ chunks/framework-733009b6474116fd.js 45.3 kB
โ chunks/main-3f7e5ffcd26ef392.js 37.9 kB
โ chunks/pages/_app-163bf496e5a12010.js 63.4 kB
โ chunks/webpack-958fb7687230bd78.js 1.89 kB
โ css/a30520c2298a663c.css 14.6 kB
โ (Static) automatically rendered as static HTML (uses no initial props)
โ (SSG) automatically generated as static HTML + JSON (uses getStaticProps)Letโs compare everything starting from the First Load JS shared by all lines:
-+ First Load JS shared by all 203 kB
++ First Load JS shared by all 163 kB
โ chunks/framework-733009b6474116fd.js 45.3 kB
- โ chunks/main-1dbce7e00d8751d5.js 38.4 kB
+ โ chunks/main-3f7e5ffcd26ef392.js 37.9 kB
- โ chunks/pages/_app-f3b27ac2556aba2f.js 101 kB
+ โ chunks/pages/_app-163bf496e5a12010.js 63.4 kB
- โ chunks/webpack-e7e126cbd02a5d11.js 1.95 kB
+ โ chunks/webpack-958fb7687230bd78.js 1.89 kB
- โ css/86d65edbc5de703f.css 16.1 kB
+ โ css/a30520c2298a663c.css 14.6 kB
Remote Docs Support
Nextra 2 already had undocumented remote docs support, and now remote docs are officially supported. You can do it with a few lines of code ๐คฏ. To render your remote docs, youโll need 2 exports:
-
buildDynamicMDXandbuildDynamicMetafromnextra/remotebuildDynamicMDX- compile your MDX data into an evaluable functionbuildDynamicMeta- create a page map for your catch-all[[...slug]].mdxroute
-
RemoteContentfromnextra/components- React component which evaluates your compiled remote MDX.
An Example of Remote Docs Fetched from GitHub
Where for current example:
user:"dimaMachina"repo:"graphql-eslint"branch:"master"docsPath:"website/src/pages/docs/"filePaths: An array of available docs file paths of your remote doc, for example:
[
"configs.mdx",
"custom-rules.mdx",
"getting-started.mdx",
"getting-started/parser-options.mdx",
"getting-started/parser.mdx",
"index.mdx"
]
import { Callout, RemoteContent } from 'nextra/components'
import { buildDynamicMDX, buildDynamicMeta } from 'nextra/remote'
import { branch, docsPath, filePaths, repo, user } from '@somewhere/graphq-eslint-data'
export async function getStaticProps({ params }) {
const path = params.slug?.join('/') ?? 'index'
const foundPath = filePaths.find(filePath => filePath.replace(/\.mdx?/, '') === path)
const url = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${foundPath}`
const response = await fetch(url)
const data = await response.text()
const { __nextra_pageMap } = await buildDynamicMeta()
const dynamicMdx = await buildDynamicMDX(data, { defaultShowCopyCode: true })
return {
props: {
__nextra_pageMap,
...dynamicMdx
}
}
}
export const getStaticPaths = () => ({
fallback: 'blocking',
paths: filePaths.map(filePath => ({
params: { slug: filePath.replace(/\.mdx?$/, '').split('/') }
}))
})
<RemoteContent components={{ Callout }} />
An Example of Remote Docsโ _meta.js That Will Fill Sidebar Items
import { createCatchAllMeta } from 'nextra/catch-all'
// filePaths: An array of available docs file paths of your remote doc
import { filePaths } from '@somewhere/graphq-eslint-data'
// Note: for remote MDX `_meta.js` should return a function instead of an object
export default () => createCatchAllMeta(filePaths)
Click to see the final result.
_app.mdx Is Removed
_app.mdx was an undocumented file, a custom workaround that was added after the v2 release to
avoid OOM (Out of Memory) problems for projects with a lot of MDX pages, he worked pretty well and
showed incredible results in improving bundle size
#1448 and
#1463.
But he confused people since no type definition can be used inside him, and no variable declarations
in your MDX too (only export const ... or export let ...).
With the v3 release, Iโm happy to announce that your bundle size is improved with the regular
_app.{js,jsx,ts,tsx} file or even without ๐คฏ!
MathJax Support
In addition to KaTeX, Nextra 3 can render LaTeX expressions with MathJax to dynamically render math in the browser.
MathJax rendering is enabled by setting renderer: 'mathjax'{:js} in your Nextra config:
const withNextra = nextra({
latex: {
renderer: 'mathjax'
}
})
With KaTeX, math is pre-rendered which means flicker-free and faster page loads. However, KaTeX does not support all of the features of MathJax, especially features related to accessibility.
Because of MathJaxโs accessibility features, the LaTeX formula is tab-accessible and has a context menu that helps screen readers reprocess math for the visually impaired.
Huge thanks to siefkenj who created the initial PR and updated docs for the MathJax section โค๏ธ
Hello ESM, Goodbye CJS
Nextra 2 was a mix of CJS/ESM bundles, Nextra 3 now is built as an ESM-only package, and the CJS
next.config.js file is no longer supported.
Bumping Minimal Node.js to 18
Node.js 18 is now the minimum supported version since Node.js 16 is EOL and no longer supported ๐.
NextraConfig In next.config.js Now Validated by Zod
To improve the development experience and avoid mistakes when users pass NextConfig option in
NextraConfig nextra config is now validated by Zod.
New Code Blocks for nextra-theme-docs and nextra-theme-blog
Code block styles were inspired by the Next.js docs website, also now they contain icons for some program languages.
Replacement shiki by shikiji
Recently Anthony Fu released
shikiji an ESM-focused rewrite of shiki.
The downside of
shikiis also that withoutcss-variablestheme, your dual themes are always rendered twice and an unneeded theme should be hidden with CSS, which I found very bad ๐ฎโ๐จ.
With Nextra 3 shikiji is used instead of shiki. github-light and github-dark themes are used
by default instead of the previously custom css-variables theme.
Migration Guide to Nextra 3
nextra
Ensure to use Node.js >= 18
Node.js 16 is EOL and no longer supported.
Ensure to use React.js >= 18
Ensure to use Next.js >= 13
CJS next.config.js is no longer supported
Use ESM next.config.mjs or add "type": "module" in your package.json file and use ESM
next.config.js.
import nextra from 'nextra'
const withNextra = nextra({
theme: 'nextra-theme-docs',
themeConfig: './theme.config.jsx'
// ... your Nextra config
})
export default withNextra({
// ... your Next.js config
})_app.mdx is no longer supported
Use _app.{js,jsx} or _app.{ts,tsx} for TypeScript projects instead.
_meta.json is no longer supported
Use _meta.{js,jsx,ts,tsx} instead.
nextra/ssg export was removed
useSSG was renamed to useData and moved to nextra/hooks, RemoteContent was moved to
nextra/components.
- import { useSSG, RemoteContent } from 'nextra/data'
+ import { useData } from 'nextra/hooks'
+ import { RemoteContent } from 'nextra/components'pageOpts.headings was removed
To retrieve TOC now you can only via toc prop in the
wrapper component which defines the layout (but a
local layout takes precedence).
import { MDXProvider } from 'nextra/mdx'
import { MySidebar, MyTOC } from '@somewhere/my-components'
export default function MyTheme({ children, pageOpts, themeConfig }) {
return (
<>
<MySidebar />
- <MDXProvider>{children}</MDXProvider>
- <MyTOC toc={pageOpts.headings} />
+ <MDXProvider components={{ wrapper: MyWrapper }}>{children}</MDXProvider>
</>
)
}
+function MyWrapper({ children, toc }) {
+ return (
+ <>
+ {children}
+ <MyTOC toc={toc} />
+ </>
+ )
+}nextra/filter-route-locale and nextra/locales exports were removed
With implementing new i18n there is no need to use
filterRouteLocale helper function and locales middleware that previously redirected to a correct
route based on locale suffix.
Tab, Card exports from nextra/components were removed
Use Tabs.Tab and Cards.Card instead.
-import { Tabs, Cards, Card, Tab } from 'nextra/components'
+import { Tabs, Cards } from 'nextra/components'
<Tabs items={[]}>
- <Tab>...</Tab>
+ <Tabs.Tab>...</Tabs.Tab>
</Tabs>
<Cards>
- <Card title="..." href="..." />
+ <Cards.Card title="..." href="..." />
</Cards>Import md/mdx files that are outside the working directory no longer supported
Use symlinks instead now.
nextraConfig.flexsearch was renamed to nextraConfig.search
$$ syntax no longer works for LaTeX
remark-math was upgraded
to v6 where the support of $$ syntax
was removed. You probably should receive the following error:
TypeError: Cannot read properties of undefined (reading 'mathFlowInside')To fix it math code block language should be used instead of $$:
- $$
+ ```math
x^2
- $$
+ ``` nextra-theme-docs
Ensure to use Next.js >= 13
Steps, Callout, Tabs, Cards and FileTree exports were removed
Import them now from nextra/components instead.
-import { Steps, Callout, Tabs, Cards, FileTree } from 'nextra-theme-docs'
+import { Steps, Callout, Tabs, Cards, FileTree } from 'nextra/components'Tailwind CSS classes prefixes now have _ prefix instead of nx-
If you have to select these classes in your custom CSS, consider updating them to new ones.
useConfig hook was split into two hooks useConfig / useThemeConfig
-import { useConfig } from 'nextra-theme-docs'
+import { useThemeConfig } from 'nextra-theme-docs'
function MyComponent() {
// Get options from `theme.config` file
- const { banner, sidebar, toc, ... } = useConfig()
+ const { banner, sidebar, toc, ... } = useThemeConfig()
}Renames of several theme.config options
-
primaryHueโcolor.hue -
primarySaturationโcolor.saturation -
i18n.textโi18n.name -
banner.textโbanner.content -
editLink.textโeditLink.content -
footer.textโfooter.content
Removes of several theme.config options
-
serverSideError -
useNextSeoProps(Setup your<head />tags viaheadoption now) -
toc.headingComponent -
sidebar.titleComponent(Use JSX elements in your_metafile instead, see below)
import { MySeparator } from '@somewhere/my-separator'
export default {
// ...
'-- Machina': {
title: <MySeparator>Machina</MySeparator>,
type: 'separator'
}
}<MatchSorterSearch /> was removed
git-url-parse and next-seo were removed
This was made to make the bundle smaller.
sidebar.toggleButton is set to true by default
toc.backToTop should use a JSX element or a React component
To disable it, you can set toc.backToTop to null.
nextra-theme-blog
Ensure to use Next.js >= 13
cusdis option in the theme.config file was removed
You need to pass cusdis options as props in Cusdis component.
Conclusion
Iโm very proud and happy to lead Nextra into the future. So many things have improved and so many more improvements are coming in the future!
If you want to help Nextra โ spread the word about Nextra in ๐ with #nextra hashtag and subscribe
on me in ๐ and GitHub.