diff --git a/.changeset/config.json b/.changeset/config.json index b924b86f31..2c965e54cb 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -10,5 +10,12 @@ "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true }, - "ignore": ["example-blog", "example-docs", "swr-site", "docs"] + "ignore": [ + "example-blog", + "example-docs", + "swr-site", + "docs", + "@nextra/prettier-config", + "@nextra/eslint-config" + ] } diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 1f27d0cf13..0000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,250 +0,0 @@ -const TAILWIND_CONFIG = { - extends: ['plugin:tailwindcss/recommended'], - rules: { - 'tailwindcss/classnames-order': 'off', // conflicts with prettier-plugin-tailwindcss - 'tailwindcss/enforces-negative-arbitrary-values': 'error', - 'tailwindcss/enforces-shorthand': 'error', - 'tailwindcss/migration-from-tailwind-2': 'error', - 'tailwindcss/no-custom-classname': 'error' - } -} - -/** @type {import('eslint').Linter.Config} */ -module.exports = { - root: true, - reportUnusedDisableDirectives: true, - ignorePatterns: ['next-env.d.ts'], - overrides: [ - // Rules for all files - { - files: '**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}', - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:import/typescript', - 'prettier' - ], - plugins: ['import', 'unicorn'], - rules: { - 'prefer-object-has-own': 'error', - 'logical-assignment-operators': [ - 'error', - 'always', - { enforceForIfStatements: true } - ], - 'no-else-return': ['error', { allowElseIf: false }], - 'no-lonely-if': 'error', - 'prefer-destructuring': [ - 'error', - { VariableDeclarator: { object: true } } - ], - 'import/no-duplicates': 'error', - 'no-negated-condition': 'off', - 'unicorn/no-negated-condition': 'error', - 'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }], - 'object-shorthand': ['error', 'always'], - 'unicorn/prefer-regexp-test': 'error', - 'unicorn/no-array-for-each': 'error', - 'unicorn/prefer-string-replace-all': 'error', - '@typescript-eslint/prefer-for-of': 'error', - // todo: enable - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/ban-ts-comment': 'off' - } - }, - // Rules for React files - { - files: '{packages,examples,docs}/**', - extends: [ - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - 'plugin:@next/next/recommended' - ], - rules: { - 'react/prop-types': 'off', - 'react/no-unknown-property': ['error', { ignore: ['jsx'] }], - 'react-hooks/exhaustive-deps': 'error', - 'react/self-closing-comp': 'error', - 'no-restricted-syntax': [ - 'error', - { - // ❌ useMemo(…, []) - selector: - 'CallExpression[callee.name=useMemo][arguments.1.type=ArrayExpression][arguments.1.elements.length=0]', - message: - "`useMemo` with an empty dependency array can't provide a stable reference, use `useRef` instead." - }, - { - // ❌ z.object(…) - selector: - 'MemberExpression[object.name=z] > .property[name=object]', - message: 'Use z.strictObject is more safe.' - } - ], - 'react/jsx-filename-extension': [ - 'error', - { extensions: ['.tsx', '.jsx'], allow: 'as-needed' } - ], - 'react/jsx-curly-brace-presence': 'error', - 'react/jsx-boolean-value': 'error' - }, - settings: { - react: { version: 'detect' } - } - }, - // Rules for TypeScript files - { - files: '**/*.{ts,tsx,cts,mts}', - extends: [ - // TODO: fix errors - // 'plugin:@typescript-eslint/recommended-requiring-type-checking' - ], - parserOptions: { - project: [ - 'packages/*/tsconfig.json', - 'docs/tsconfig.json', - 'tsconfig.eslint.json' - ] - }, - rules: { - '@typescript-eslint/no-unnecessary-type-assertion': 'error', - '@typescript-eslint/consistent-type-imports': 'error', - '@typescript-eslint/non-nullable-type-assertion-style': 'error', - '@typescript-eslint/prefer-optional-chain': 'error' - } - }, - // ⚙️ nextra-theme-docs - { - ...TAILWIND_CONFIG, - files: 'packages/nextra-theme-docs/**', - plugins: ['typescript-sort-keys'], - settings: { - tailwindcss: { - config: 'packages/nextra-theme-docs/tailwind.config.js', - callees: ['cn'], - whitelist: [ - 'nextra-breadcrumb', - 'nextra-bleed', - 'nextra-menu-desktop', - 'nextra-menu-mobile' - ] - } - }, - rules: { - ...TAILWIND_CONFIG.rules, - 'no-restricted-imports': [ - 'error', - { - name: 'next/link', - message: 'Use local instead' - } - ] - } - }, - // ⚙️ nextra-theme-blog - { - ...TAILWIND_CONFIG, - files: 'packages/nextra-theme-blog/**', - settings: { - tailwindcss: { - config: 'packages/nextra-theme-blog/tailwind.config.js', - whitelist: ['subheading-', 'post-item', 'post-item-more'] - } - } - }, - // ⚙️ nextra - { - ...TAILWIND_CONFIG, - files: 'packages/nextra/**', - settings: { - tailwindcss: { - config: 'packages/nextra-theme-docs/tailwind.config.js', - callees: ['cn'], - whitelist: ['nextra-code-block', 'nextra-filetree'] - } - } - }, - // ⚙️ Docs - { - ...TAILWIND_CONFIG, - files: 'docs/**', - settings: { - tailwindcss: { - config: 'docs/tailwind.config.js', - callees: ['cn'], - whitelist: ['dash-ring', 'theme-1', 'theme-2', 'theme-3', 'theme-4'] - }, - next: { rootDir: 'docs' } - } - }, - // ⚙️ SWR-site example - { - ...TAILWIND_CONFIG, - files: 'examples/swr-site/**', - settings: { - tailwindcss: { - config: 'examples/swr-site/tailwind.config.js' - }, - next: { rootDir: 'examples/swr-site' } - } - }, - // ⚙️ blog example - { - files: 'examples/blog/**', - settings: { - next: { rootDir: 'examples/blog' } - } - }, - // ⚙️ docs example - { - files: 'examples/docs/**', - settings: { - next: { rootDir: 'examples/docs' } - } - }, - { - files: [ - 'prettier.config.js', - 'postcss.config.js', - 'tailwind.config.js', - 'next.config.js', - '.eslintrc.cjs' - ], - env: { - node: true - } - }, - { - files: 'packages/{nextra,nextra-theme-docs,nextra-theme-blog}/**', - rules: { - // disable rule because we don't have pagesDir in above folders - '@next/next/no-html-link-for-pages': 'off' - } - }, - { - files: 'packages/nextra/src/**', - rules: { - 'no-restricted-imports': [ - 'error', - { - patterns: [ - { - group: ['fs', 'node:fs'], - message: 'Use `graceful-fs` instead' - } - ] - } - ] - } - }, - { - files: ['**/*.d.ts'], - rules: { - 'no-var': 'off' - } - } - ] -} diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..799e52164e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' +--- + +**Describe the bug** A clear and concise description of what the bug is. + +**To Reproduce** Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** A clear and concise description of what you expected to +happen. + +**Screenshots** If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..b75642ba2f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' +--- + +**Is your feature request related to a problem? Please describe.** A clear and +concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** A clear and concise description of what you +want to happen. + +**Describe alternatives you've considered** A clear and concise description of +any alternative solutions or features you've considered. + +**Additional context** Add any other context or screenshots about the feature +request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..f44e8da8db --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ + + +## Why: + +Closes: + + + +## What's being changed (if available, include any code snippets, screenshots, or gifs): + + + +## Check off the following: + +- [ ] I have reviewed my changes in staging, available via the **View + deployment** link in this PR's timeline (this link will be available after + opening the PR). diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 916372b771..cf49918293 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,14 +2,11 @@ name: Lint on: pull_request: - branches: [main, v3] + branches: [main, v4-v2] jobs: lint: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18] steps: - name: Cancel Previous Runs @@ -17,15 +14,24 @@ jobs: with: access_token: ${{ github.token }} - - name: Checkout Master + - name: Check out code uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2.4.0 + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Install pnpm + uses: pnpm/action-setup@v4 - - name: Use Node.js ${{ matrix.node-version }} + - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: .node-version cache: pnpm - name: Install Dependencies diff --git a/.github/workflows/nextjs-bundle-analysis.yml b/.github/workflows/nextjs-bundle-analysis.yml index 7d7795cc51..5ca664c4bc 100644 --- a/.github/workflows/nextjs-bundle-analysis.yml +++ b/.github/workflows/nextjs-bundle-analysis.yml @@ -3,7 +3,7 @@ name: Next.js Bundle Analysis on: pull_request: push: - branches: [main, v3, v4-v2] + branches: [main, v4-v2] workflow_dispatch: permissions: @@ -24,14 +24,24 @@ jobs: with: access_token: ${{ github.token }} - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2.4.0 + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Install pnpm + uses: pnpm/action-setup@v4 - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: 18 + node-version-file: .node-version cache: pnpm - name: Install Dependencies @@ -66,7 +76,7 @@ jobs: path: ${{ matrix.package }}/.next/analyze/__bundle_analysis.json - name: Download Base Branch Bundle Stats - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v8 if: success() && github.event.number with: name: bundle-${{ matrix.package == 'docs' && 'docs' || 'swr-site' }} @@ -101,7 +111,7 @@ jobs: echo EOF >> $GITHUB_OUTPUT - name: Add comment to PR - uses: marocchino/sticky-pull-request-comment@v2.9.0 + uses: marocchino/sticky-pull-request-comment@v2.9.1 if: success() && github.event.number with: header: 'bundle-analysis-${{ matrix.package }}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d625797fed..65fb215f86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: push: - branches: [main, v3] + branches: [main, v4-v2] jobs: release: @@ -11,15 +11,30 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.12.1 + with: + access_token: ${{ github.token }} + + - name: Check out code + uses: actions/checkout@v4 + + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - - uses: pnpm/action-setup@v2.4.0 + - name: Install pnpm + uses: pnpm/action-setup@v4 - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: 18 - cache: 'pnpm' + node-version-file: .node-version + cache: pnpm - name: Install Dependencies run: pnpm i diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ae026d774f..23d17778d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,15 +2,14 @@ name: Test on: pull_request: - branches: [main, v3] + branches: [main, v4-v2] jobs: test: name: Test (${{matrix.os}}) - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: - node-version: [18] os: [ubuntu-latest, windows-latest] steps: @@ -19,15 +18,24 @@ jobs: with: access_token: ${{ github.token }} - - name: Checkout Master + - name: Check out code uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2.4.0 + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Install pnpm + uses: pnpm/action-setup@v4 - - name: Use Node.js ${{ matrix.node-version }} + - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: .node-version cache: pnpm - name: Install Dependencies diff --git a/.gitignore b/.gitignore index dd78c815b0..158fa1c3be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,18 @@ .DS_Store .next/ +.tsup/ node_modules/ *.log dist/ .turbo/ out/ -# Theme styles -packages/nextra-theme-*/style.css -# Stork related -*/**/public/*.st -*/**/public/*.toml - -.vercel +.vercel/ .idea/ .eslintcache .env tsup.config.bundled* tsconfig.tsbuildinfo +_pagefind/ +docs/public/sitemap.xml diff --git a/.node-version b/.node-version new file mode 100644 index 0000000000..2bd5a0a98a --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22 diff --git a/.npmrc b/.npmrc index 74021ef94e..bacf9bc0c3 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ strict-peer-dependencies=false shell-emulator=true +enable-pre-post-scripts=true diff --git a/.prettierignore b/.prettierignore index b6f8b60a9c..7300724fd8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,10 +1,17 @@ pnpm-lock.yaml .changeset/*.md +CHANGELOG.md +generated-page-map.ts -# process.env assignement should be first -packages/nextra/__test__/locale-with-base-path.test.ts +examples/swr-site/nextra-remote-filepaths/*.json -# bellow files are broken since prettier doesn't support MDX2 -examples/swr-site/pages/remote/graphql-eslint/\[\[...slug\]\].mdx -examples/swr-site/pages/blog.en-US.mdx -docs/pages/docs/guide/advanced/latex.mdx +examples/docs/src/content/features/mdx.mdx +docs/app/docs/advanced/latex/page.mdx +docs/app/docs/advanced/mermaid/page.mdx +docs/app/docs/advanced/npm2yarn/page.mdx +docs/app/docs/built-ins/cards/page.mdx +docs/app/docs/guide/ssg/page.mdx +docs/app/docs/guide/syntax-highlighting/page.mdx + +# contains mdx comments +docs/app/docs/blog-theme/start/page.mdx diff --git a/README.md b/README.md index 27acc27c13..a7552780f8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ from Next.js. ## Documentation -[https://nextra.site](https://nextra.site) +https://nextra.site ## Development @@ -15,39 +15,44 @@ The Nextra repository uses [PNPM Workspaces](https://pnpm.io/workspaces) and [Turborepo](https://github.com/vercel/turborepo). To install dependencies, run `pnpm install` in the project root directory. -### Build Nextra Core +### Build `nextra` ```bash -cd packages/nextra -pnpm build +pnpm --filter nextra build ``` -Watch mode: `pnpm dev` +Watch mode: `pnpm --filter nextra dev` -### Build Nextra Theme +### Build `nextra-theme-docs` ```bash -cd packages/nextra-theme-docs -pnpm build +pnpm --filter nextra-theme-docs build ``` -| Command | Description | -| ----------------- | ------------------------ | -| pnpm dev | Watch mode | -| pnpm dev:layout | Watch mode (layout only) | -| pnpm dev:tailwind | Watch mode (style only) | - ### Development You can also debug them together with a website locally. For instance, to start -examples/docs locally, run +`examples/docs` locally, run ```bash -cd examples/docs -pnpm dev +pnpm --filter example-docs dev ``` -Any change to example/docs will be re-rendered instantly. +Any change to `example/docs` will be re-rendered instantly. If you update the core or theme packages, a rebuild is required. Or you can use -the watch mode for both nextra and the theme in separated terminals. +the watch mode for both Nextra and the theme in separated terminals. + +## Sponsors + +
+ + GraphQL Hive preview + + + Speakeasy preview + + + xyflow preview + +
diff --git a/docs/app/_components/features/index.tsx b/docs/app/_components/features/index.tsx new file mode 100644 index 0000000000..9870a8e275 --- /dev/null +++ b/docs/app/_components/features/index.tsx @@ -0,0 +1,59 @@ +import { ArrowRightIcon } from '@components/icons' +import cn from 'clsx' +import Link from 'next/link' +import type { ComponentProps, FC, ReactNode } from 'react' +import { MotionDiv } from '../framer-motion' +import styles from './style.module.css' + +export const Feature: FC< + { + large?: boolean + centered?: boolean + children: ReactNode + lightOnly?: boolean + href?: string + index: number + } & ComponentProps +> = ({ + large, + centered, + children, + lightOnly, + className, + href, + index, + ...props +}) => { + return ( + + {children} + {href && ( + + + + )} + + ) +} + +export const Features: FC<{ children: ReactNode }> = ({ children }) => { + return
{children}
+} diff --git a/docs/components/features/style.module.css b/docs/app/_components/features/style.module.css similarity index 98% rename from docs/components/features/style.module.css rename to docs/app/_components/features/style.module.css index 4e5a9aed7e..30723928ab 100644 --- a/docs/components/features/style.module.css +++ b/docs/app/_components/features/style.module.css @@ -62,7 +62,8 @@ .feature:hover .link { opacity: 1; } - .feature .link:hover { + .feature .link:hover, + .link:focus { transform: scale(1.05); color: rgba(255, 255, 255, 1); background-color: rgba(64, 64, 64, 0.39); diff --git a/docs/components/features/themes-animation.tsx b/docs/app/_components/features/themes-animation.tsx similarity index 99% rename from docs/components/features/themes-animation.tsx rename to docs/app/_components/features/themes-animation.tsx index 0848b55cc9..a7df793759 100644 --- a/docs/components/features/themes-animation.tsx +++ b/docs/app/_components/features/themes-animation.tsx @@ -1,4 +1,6 @@ -export function ThemesAnimation(props) { +import type { FC } from 'react' + +export const ThemesAnimation: FC = props => { return ( { + const [active, setActive] = useState('') + + return ( +
+
+ {LANGUAGES.map(({ lang }) => ( + setActive(lang)} + className={cn(styles.file, active === lang && styles.active)} + > + /{lang}/hello.mdx + + ))} +
+ +
+ {LANGUAGES.map(({ lang, name }) => ( +
setActive(lang)} + className={cn( + 'relative cursor-default px-4 py-1.5 whitespace-nowrap select-none', + active === lang + ? 'x:text-primary-600 x:bg-primary-50 x:dark:bg-primary-500/10' + : 'text-gray-800 dark:text-gray-100' + )} + > + {name} +
+ ))} +
+
+ ) +} diff --git a/docs/app/_meta.global.tsx b/docs/app/_meta.global.tsx new file mode 100644 index 0000000000..401676729f --- /dev/null +++ b/docs/app/_meta.global.tsx @@ -0,0 +1,210 @@ +import type { MetaRecord } from 'nextra' +import { LinkArrowIcon } from 'nextra/icons' +import type { FC, ReactNode } from 'react' +import { useMDXComponents } from '../mdx-components' + +// eslint-disable-next-line react-hooks/rules-of-hooks -- isn't react hook +const { code: Code } = useMDXComponents() + +const ExternalLink: FC<{ children: ReactNode }> = ({ children }) => { + return ( + <> + {children}  + + + ) +} + +const FILE_CONVENTIONS: MetaRecord = { + _: { + type: 'separator', + title: 'Files' + }, + 'page-file': page.mdx, + 'meta-file': _meta.js, + _2: { + href: 'https://nextjs.org/docs/app/api-reference/file-conventions/page', + title: page.jsx + }, + _3: { + href: 'https://nextjs.org/docs/app/api-reference/file-conventions/layout', + title: layout.jsx + }, + _4: { + type: 'separator', + title: 'Top-Level Files' + }, + 'mdx-components-file': mdx-components.js, + _5: { + type: 'separator', + title: 'Top-Level Folders' + }, + 'content-directory': content, + 'src-directory': src, + _6: { + href: 'https://nextjs.org/docs/app/getting-started/installation?utm_source=nextra.site&utm_medium=referral&utm_campaign=sidebar#create-the-app-directory', + title: app + }, + _7: { + href: 'https://nextjs.org/docs/app/building-your-application/optimizing/static-assets?utm_source=nextra.site&utm_medium=referral&utm_campaign=sidebar', + title: public + } +} + +const GUIDE: MetaRecord = { + markdown: '', + 'syntax-highlighting': '', + link: '', + image: '', + ssg: '', + i18n: '', + 'custom-css': '', + 'static-exports': '', + search: '', + 'github-alert-syntax': '', + turbopack: '', + _: { + title: 'Deploying', + href: 'https://nextjs.org/docs/app/building-your-application/deploying?utm_source=nextra.site&utm_medium=referral&utm_campaign=sidebar' + } +} + +const ADVANCED: MetaRecord = { + npm2yarn: '', + mermaid: '', + 'tailwind-css': '', + latex: '', + table: '', + typescript: '', + remote: '', + playground: { + theme: { + layout: 'full', + toc: false + } + } +} + +const NEXTRA_BUILTINS: MetaRecord = { + _: { + type: 'separator', + title: 'Layout Components' + }, + banner: '', + head: '', + search: '', + __: { + type: 'separator', + title: 'Content Components' + } +} + +const BLOG_THEME: MetaRecord = { + start: '', + 'get-posts-and-tags': '', + // prettier-ignore + posts: <>/posts Page, + // prettier-ignore + tags: <>/tags/:id Page, + // prettier-ignore + rss: <>/rss.xml Route +} + +export default { + index: { + type: 'page', + display: 'hidden' + }, + docs: { + type: 'page', + title: 'Documentation', + items: { + index: '', + 'file-conventions': { items: FILE_CONVENTIONS }, + guide: { items: GUIDE }, + advanced: { items: ADVANCED }, + 'built-ins': { items: NEXTRA_BUILTINS }, + _: { + type: 'separator', + title: 'Themes' + }, + 'docs-theme': { + items: { + start: '', + 'built-ins': { + items: { + layout: '' + } + } + } + }, + 'blog-theme': { items: BLOG_THEME }, + 'custom-theme': '', + __: { + type: 'separator', + title: 'More' + }, + 'about-link': { + title: 'About Nextra', + href: '/about' + }, + 'next.js-link': { + title: 'Next.js Docs', + href: 'https://nextjs.org?utm_source=nextra.site&utm_medium=referral&utm_campaign=sidebar' + }, + 'migration-from-v3': { + title: 'Migration from Nextra v3', + href: 'https://the-guild.dev/blog/nextra-4?utm_source=nextra.site&utm_campaign=sidebar&utm_content=sidebar_link#nextra-theme-docs-changes' + } + } + }, + versions: { + type: 'menu', + title: 'Versions', + items: { + _3: { + title: 'Nextra v3 Docs', + href: 'https://nextra-v2-7hslbun8z-shud.vercel.app' + }, + _2: { + title: 'Nextra v2 Docs', + href: 'https://nextra-v2-oe0zrpzjp-shud.vercel.app' + } + } + }, + blog: { + type: 'page', + theme: { + typesetting: 'article', + toc: false + } + }, + about: { + type: 'page', + theme: { + typesetting: 'article' + } + }, + showcase: { + type: 'page', + theme: { + typesetting: 'article', + layout: 'full', + timestamp: false, + toc: false + } + }, + sponsors: { + type: 'page', + theme: { + typesetting: 'article', + layout: 'full', + timestamp: false, + toc: false + } + } +} diff --git a/docs/app/about/page.mdx b/docs/app/about/page.mdx new file mode 100644 index 0000000000..8c5cf5cb09 --- /dev/null +++ b/docs/app/about/page.mdx @@ -0,0 +1,77 @@ +--- +sidebarTitle: About +description: + Learn about Nextra's history, team, and contributors, and explore how + open-source technologies power Nextra's features. +--- + +import { Image } from 'nextra/components' +import { cloneElement } from 'react' + +export default function MdxLayout(props) { + return cloneElement(props.children, { + components: { + img: props => + } + }) +} + +# About Nextra + +Nextra was initially created by [Vercel](https://vercel.com) members +[Shu Ding](https://twitter.com/shuding_) and +[Paco Coursey](https://twitter.com/pacocoursey) in 2020. Since 2021, +[Yixuan Xu](https://twitter.com/yixuanxu94) contributed tremendously to the +project. + +In 2022, [Dimitri Postolov](https://twitter.com/dimaMachina_) from +[The Guild](https://the-guild.dev) joined the core team to help with the +development of Nextra 2. + +In 2024 Nextra 3 was released, current primary maintainer Dimitri Postolov fully +developed it, and [Oscar Xie](https://github.com/87xie) +[actively contributed](https://github.com/shuding/nextra/pulls?q=sort%3Aupdated-desc+is%3Apr+author%3A87xie+is%3Aclosed+created%3A%3C2024-10-03) +to this release. + +In 2025 Nextra 4 with [App Router](https://nextjs.org/docs/app) support was +released, Dimitri Postolov fully developed it too. + +## Team + +Currently, the project is maintained by Dimitri Postolov. You can check out +[the full list of contributors](https://github.com/shuding/nextra/graphs/contributors) +on GitHub. + +## Credits + +Nextra is powered by these incredible open source projects: + +- https://reactjs.org +- https://nextjs.org +- https://turbo.build +- https://mdxjs.com +- https://pnpm.io +- https://tailwindcss.com +- https://github.com/pacocoursey/next-themes +- https://github.com/shikijs/shiki +- https://github.com/cloudcannon/pagefind +- https://github.com/atomiks/rehype-pretty-code +- https://github.com/Brooooooklyn/simple-git +- https://github.com/francoismassart/eslint-plugin-tailwindcss + +## Design assets + +Feel free to use the Nextra logo and other assets in your project. But please +don't modify the logo, and don't use the logo to represent your project or +product. + +| Name | Description | Preview | +| :---------: | :----------------------------------------------: | :-----------------------------------: | +| Icon | Useful for favicons, app icons, link icons, etc. | ![Nextra icon](../icon.svg) | +| Logo | Full Nextra logo | ![Nextra logo](/logo.svg) | +| Social Card | The Nextra social card | ![Nextra card](/opengraph-image.jpeg) | + +## License + +The Nextra project and themes are licensed under +[the MIT license](https://github.com/shuding/nextra/blob/main/LICENSE). diff --git a/docs/app/blog/page.mdx b/docs/app/blog/page.mdx new file mode 100644 index 0000000000..d16d55fcde --- /dev/null +++ b/docs/app/blog/page.mdx @@ -0,0 +1,63 @@ +--- +asIndexPage: true +description: + Stay updated with the latest news and updates from the Nextra team, including + new releases, features, and community highlights. +--- + +import { Link } from 'nextra-theme-docs' + +# Blog + + + +export function BlogPage() { + return [ + { + route: + 'https://the-guild.dev/blog/nextra-4?utm_source=nextra.site&utm_campaign=blog_page&utm_content=blog_link', + title: 'Nextra 4', + description: + 'App Router support, Turbopack support, compiled by React Compiler, new Rust-powered search Pagefind, RSC i18n, server/client components, smallest bundle size EVER for a Nextra-powered website, GitHub Alert Syntax, new _meta.global file and more.', + date: '2024-01-13' + }, + { + route: + 'https://the-guild.dev/blog/nextra-3?utm_source=nextra.site&utm_campaign=blog_page&utm_content=blog_link', + title: 'Nextra 3 – Your Favourite MDX Framework, Now on 🧪 Steroids', + description: + 'MDX 3, new i18n, new _meta files with JSX support, more powerful TOC, remote MDX, better bundle size, MathJax, new code block styles, shikiji, ESM-only and more.', + date: '2023-12-12' + }, + { + route: + 'https://the-guild.dev/blog/nextra-2?utm_source=nextra.site&utm_campaign=blog_page&utm_content=blog_link', + title: 'Nextra 2 – Next.js Static Site Generator', + description: + 'Here are what the new version of Nextra 2 Framework includes.', + date: '2023-01-24' + } + ].map(page => ( +
+

{page.title}

+

+ {page.description}{' '} + {page.date && Read more} +

+ {page.date ? ( + + ) : ( + Coming soon! + )} +
+ )) +} diff --git a/docs/app/docs/advanced/customize-the-cascade-layers/page.mdx b/docs/app/docs/advanced/customize-the-cascade-layers/page.mdx new file mode 100644 index 0000000000..d4c53b5d46 --- /dev/null +++ b/docs/app/docs/advanced/customize-the-cascade-layers/page.mdx @@ -0,0 +1,58 @@ +--- +sidebarTitle: Customize Cascade Layers +--- + +import { Steps } from 'nextra/components' + +# Customize the Cascade Layers + +In some scenarios, you may need more control over the Nextra predefined CSS to +avoid unintended overrides of styles within cascade layers. Below is an example +of how `nextra-theme-docs` uses +[postcss-import](https://github.com/postcss/postcss-import) to place predefined +CSS into a specified cascade layer: + + + +## Install `postcss-import` + +Install `postcss-import` and add it to `postcss.config.mjs`: + +```js filename="postcss.config.mjs" +export default { + plugins: { + 'postcss-import': {} + // ... your other PostCSS plugins (e.g., `autoprefixer`, `cssnano`) + } +} +``` + +## Set up the cascade layers + +In your CSS file (e.g. `styles.css`), import the `nextra-docs-theme` CSS and +specify the layers: + +```css filename="styles.css" +@layer nextra, my-base; + +@import 'nextra-theme-docs/dist/style.css' layer(nextra); + +@layer my-base { + /* my base styles */ +} +``` + +## Import your CSS file + +Import your CSS file at the top-level layout of your application (e.g. +`app/layout.jsx`) to apply the styles. + +```jsx filename="app/layout.jsx" +import '../path/to/your/styles.css' + +export default async function RootLayout({ children }) { + // ... Your layout logic here +} +``` + + diff --git a/docs/app/docs/advanced/latex/page.mdx b/docs/app/docs/advanced/latex/page.mdx new file mode 100644 index 0000000000..d5a9b2fc30 --- /dev/null +++ b/docs/app/docs/advanced/latex/page.mdx @@ -0,0 +1,230 @@ +--- +icon: FormulaIcon +--- + +import { compileMdx } from 'nextra/compile' +import { MDXRemote } from 'nextra/mdx-remote' +import { + MathJax, + MathJaxContext, +} from 'nextra/components' + +{/* is unsupported in Metadata API https://nextjs.org/docs/app/api-reference/functions/generate-metadata#unsupported-metadata */} + + +# LaTeX + +Nextra can use [KaTeX](https://katex.org) to pre-render LaTeX expressions +directly in MDX or [MathJax](https://mathjax.org) to dynamically render math in +the browser. To enable LaTeX support, you must enable the `latex` option in your +`next.config.mjs` file: + +```js filename="next.config.mjs" {4} +import nextra from 'nextra' + +const withNextra = nextra({ + latex: true +}) + +export default withNextra() +``` + +A value of `true{:js}` will use KaTeX as the math renderer. To explicitly +specify the renderer, you may instead provide an object +`{ renderer: 'katex' }{:js}` or `{ renderer: 'mathjax' }{:js}` as the value to +`latex: ...`. + +When enabled, the required CSS and fonts will be automatically included in your +site, and you can start writing math expressions by enclosing inline math in +`$...$` or display math in a `math`-labeled fenced code block: + +~~~mdx +```math +\int x^2 +``` +~~~ + +## Example + +For example, the following Markdown code: + +~~~md filename="page.md" +The **Pythagorean equation** is $a=\sqrt{b^2 + c^2}$ and the quadratic formula: + +```math +x=\frac{-b\pm\sqrt{b^2-4ac}}{2a} +``` +~~~ + +will be rendered as: + +
+The **Pythagorean equation** is $a=\sqrt{b^2 + c^2}$ and the quadratic formula: + +```math +x=\frac{-b\pm\sqrt{b^2-4ac}}{2a} +``` + +
+ +You can still use [Markdown and MDX syntax](../markdown) in the same line as +your LaTeX expression. + +> [!TIP] +> +> If you want to display `$` in your content instead of rendering it as an +> equation, you can escape it with a backslash (`\`). For example `\$e = mc^2\$` +> will be rendered as \$e = mc^2\$. + +## API + +### KaTeX + +`rehype-katex` is used to pre-render LaTeX expressions in your content. You can +pass supported [KaTeX options](https://katex.org/docs/options) via the `options` +key in your Nextra config. For example, to add a macro `\RR` that renders as +`\mathbb{R}` you could use the following configuration. + +```js filename="next.config.mjs" {4-8} +const withNextra = nextra({ + latex: { + renderer: 'katex', + options: { + macros: { + '\\RR': '\\mathbb{R}' + } + } + } +}) +``` + +See [KaTeX's documentation](https://katex.org/docs/supported) for a list of +supported commands. + +#### Apply KaTeX styles + +Add `{:jsx}` +inside `` element in your root `layout` file since `{:jsx}` +[isn't supported with Next.js Metadata API](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#unsupported-metadata). + +Alternatively, you can include `{:jsx}` directly in your MDX file: + +~~~mdx filename="katex.mdx" + + +# My page with single usage of KaTeX + +```math +\int_2^3x^3\,\mathrm{d}x +``` +~~~ + +### MathJax + +When MathJax is enabled (by setting `latex: { renderer: 'mathjax' }{:js}`) math +is rendered on page load via +[`better-react-mathjax`](https://github.com/fast-reflexes/better-react-mathjax) +instead of being pre-rendered. By default, **MathJax is served via the MathJax +CDN** instead of the files being directly included in your site.[^1] + +[^1]: + This can be changed by setting + [`{ options: { src: ... } }{:js}`](https://github.com/fast-reflexes/better-react-mathjax#src-string--undefined) + in the Nextra config. + +MathJax rendering is enabled by setting `renderer: 'mathjax'{:js}` in your +Nextra config. + +```js filename="next.config.mjs" {3} +const withNextra = nextra({ + latex: { + renderer: 'mathjax' + } +}) +``` + +You can pass additional options to `better-react-mathjax` via the `options` key +in your Nextra config. The `config: ...` option sets the +[MathJax configuration](https://docs.mathjax.org/en/latest/options/index.html). +However, note that you can only pass serializable options to +`better-react-mathjax` via the `options` key in your Nextra config.[^2] + +[^2]: + To pass non-serializable objects like Functions, you must use the + `{:jsx}` component directly in your source. + +For example, to configure MathJax to render `\RR` as `\mathbb{R}` you could use +the following configuration. + +```js filename="next.config.mjs" {4-12} +const withNextra = nextra({ + latex: { + renderer: 'mathjax', + options: { + config: { + tex: { + macros: { + RR: '\\mathbb{R}' + } + } + } + } + } +}) +``` + +#### MathJax CDN + +By default, MathJax is served via the MathJax CDN. To serve files from another +location (including locally in your project), you must pass the `src: ...` +option to the latex config. See the +[better-react-mathjax documentation](https://github.com/fast-reflexes/better-react-mathjax#src-string--undefined) +for details about the `src` option. Additionally, you may need to copy the +MathJax distribution into your `/public` folder for it to be served locally. + +## KaTeX vs. MathJax + +With KaTeX, math is pre-rendered which means flicker-free and faster page loads. +However, KaTeX does not support all the features of MathJax, especially +features related to accessibility. + +The following two examples show the same formula rendered with KaTeX (first) and +MathJax (second). + +```math +\int_2^3x^3\,\mathrm{d}x +``` + + + +Because of MathJax's accessibility features, the second formula is +tab-accessible and has a context menu that helps screen readers reprocess math +for the visually impaired. + +export async function MathJaxExample() { + const rawMdx = `~~~math +\\int_2^3x^3\\,\\mathrm{d}x +~~~` + const rawJs = await compileMdx( + rawMdx, + { + latex: { + renderer: 'mathjax', + options: { + config: { + tex: { + macros: { + RR: '\\mathbb{R}' + } + } + } + } + }, + } + ) + + return +} diff --git a/docs/app/docs/advanced/mermaid/page.mdx b/docs/app/docs/advanced/mermaid/page.mdx new file mode 100644 index 0000000000..0ea4c274a8 --- /dev/null +++ b/docs/app/docs/advanced/mermaid/page.mdx @@ -0,0 +1,51 @@ +--- +icon: DiagramIcon +--- + +import { compileMdx } from 'nextra/compile' +import { Mermaid } from 'nextra/components' +import { MDXRemote } from 'nextra/mdx-remote' + +# Mermaid + +Nextra supports [mermaid](https://mermaid.js.org) diagrams. Like in GitHub you +can use it in your Markdown files by using the `mermaid` code block language. +Out of the box, Nextra uses +[`@theguild/remark-mermaid`](https://npmjs.com/package/@theguild/remark-mermaid) +package that replaces the code block with the `` component. + +## Example + + + +export async function Demo() { + const mermaidCodeblock = `\`\`\`mermaid +graph TD; +subgraph AA [Consumers] +A[Mobile app]; +B[Web app]; +C[Node.js client]; +end +subgraph BB [Services] +E[REST API]; +F[GraphQL API]; +G[SOAP API]; +end +Z[GraphQL API]; +A --> Z; +B --> Z; +C --> Z; +Z --> E; +Z --> F; +Z --> G; +\`\`\`` + const rawJs = await compileMdx(`${mermaidCodeblock} + +## Usage + +~~~md filename="Markdown" +${mermaidCodeblock} +~~~ +`) + return +} diff --git a/docs/app/docs/advanced/npm2yarn/page.mdx b/docs/app/docs/advanced/npm2yarn/page.mdx new file mode 100644 index 0000000000..fcd7f2b2ba --- /dev/null +++ b/docs/app/docs/advanced/npm2yarn/page.mdx @@ -0,0 +1,35 @@ +--- +icon: TerminalIcon +--- + +import { compileMdx } from 'nextra/compile' +import { Tabs } from 'nextra/components' +import { MDXRemote } from 'nextra/mdx-remote' + +# Npm2Yarn + +Nextra uses +[`@theguild/remark-npm2yarn`](https://npmjs.com/package/@theguild/remark-npm2yarn) +package that replaces the code block that has `npm2yarn` metadata with +[`` and `` components](/docs/guide/built-ins/tabs) from +`nextra/components`. + +The chosen tab is saved in the local storage, which will be chosen in future +page renders. + +## Example + + + +export async function Page() { + const codeBlock = `\`\`\`sh npm2yarn +npm i -D @graphql-eslint/eslint-plugin +\`\`\`` + const rawJs = await compileMdx(`${codeBlock} +## Usage + +~~~md filename="Markdown" /npm2yarn/ +${codeBlock} +~~~`) + return +} diff --git a/docs/app/docs/advanced/page.mdx b/docs/app/docs/advanced/page.mdx new file mode 100644 index 0000000000..cffd00f13e --- /dev/null +++ b/docs/app/docs/advanced/page.mdx @@ -0,0 +1,31 @@ +--- +asIndexPage: true +--- + +import { + CloudIcon, + DiagramIcon, + FormulaIcon, + TableIcon, + TailwindIcon +} from '@components/icons' +import { Cards } from 'nextra/components' +import { TerminalIcon, TypeScriptIcon } from 'nextra/icons' +import { MDXRemote } from 'nextra/mdx-remote' +import { createIndexPage, getPageMap } from 'nextra/page-map' + +# Advanced + + diff --git a/docs/app/docs/advanced/playground/_demo.tsx b/docs/app/docs/advanced/playground/_demo.tsx new file mode 100644 index 0000000000..77a7d55142 --- /dev/null +++ b/docs/app/docs/advanced/playground/_demo.tsx @@ -0,0 +1,90 @@ +'use client' + +import { Code, Mermaid, Playground, Pre, Tabs } from 'nextra/components' +import { MdxIcon } from 'nextra/icons' +import type { FC } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useMDXComponents } from '../../../../mdx-components' + +export const Demo: FC = () => { + const [rawMdx, setRawMdx] = + useState(`Playground components allow you to write Nextra compatible MDX that renders only on the client. It's modeled after the functionality found in [MDX Playground](https://mdxjs.com/playground). + +In some instances where remote loading MDX is not an option, this may work as a great alternative. + +Here's an example of a codeblock. + +\`\`\`ts +console.log("Hello world, this is a playground component!"); +\`\`\` + +## Caveats + +Due to the purely client-side nature of this component, features "Table of Contents" and "Frontmatter" will not work. + +## Mermaid Example + +\`\`\`mermaid +graph TD +subgraph AA [Consumers] +A[Mobile App] +B[Web App] +C[Node.js Client] +end +subgraph BB [Services] +E[REST API] +F[GraphQL API] +G[SOAP API] +end +Z[GraphQL API] +A --> Z +B --> Z +C --> Z +Z --> E +Z --> F +Z --> G +\`\`\``) + const handleInput = useCallback(e => { + setRawMdx(e.currentTarget.textContent ?? '') + }, []) + + const spanRef = useRef(null!) + const initialRender = useRef(false) + + useEffect(() => { + if (!initialRender.current) { + initialRender.current = true + spanRef.current.textContent = rawMdx + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps -- only on mount + + return ( +
+
}
+      >
+        
+          
+        
+      
+
+ + Loading playground... +
+ } + source={rawMdx} + components={useMDXComponents({ Mermaid, $Tabs: Tabs })} + /> +
+ + ) +} diff --git a/docs/app/docs/advanced/playground/page.mdx b/docs/app/docs/advanced/playground/page.mdx new file mode 100644 index 0000000000..97a68f12cb --- /dev/null +++ b/docs/app/docs/advanced/playground/page.mdx @@ -0,0 +1,52 @@ +import { Demo } from './_demo' + +# Playground + + + +## Usage + +```mdx filename="Basic Usage" +import { Playground } from 'nextra/components' + +# Playground + +Below is a playground component. It mixes into the rest of your MDX perfectly. + +

}} +/> +``` + +You may also specify a fallback component like so: + +```mdx filename="Usage with Fallback" +import { Playground } from 'nextra/components' + +

}} + fallback={
Loading playground...
} +/> +``` + +## Avoiding unstyled outputs + +To prevent unstyled elements, import `useMDXComponents` from your +`mdx-components` file. Call this function and pass the returned components to +the `components` prop. You can also include your custom components as the first +argument: + +```mdx {1,6-8} +import { Playground } from 'nextra/components' +import { useMDXComponents } from '../path/to/my/mdx-components' + +

+ })} + fallback={
Loading playground...
} +/> +``` diff --git a/docs/app/docs/advanced/remote/page.mdx b/docs/app/docs/advanced/remote/page.mdx new file mode 100644 index 0000000000..69cefd60f6 --- /dev/null +++ b/docs/app/docs/advanced/remote/page.mdx @@ -0,0 +1,63 @@ +--- +icon: CloudIcon +--- + +import fs from 'node:fs/promises' +import { compileMdx } from 'nextra/compile' +import { Steps } from 'nextra/components' +import { MDXRemote } from 'nextra/mdx-remote' + +export async function Example() { + const filename = '/graphql-eslint/[[...slug]]/page.tsx' + const pageContent = await fs.readFile( + `../examples/swr-site/app/[lang]/remote${filename}`, + 'utf8' + ) + const rawJs = + await compileMdx(`~~~jsx filename="app${filename}" {27} showLineNumbers +${pageContent + .replace('// @ts-expect-error -- fixme\n', '') + .replace( + "lang: 'en',\n ...(route && { slug: route.split('/') })", + "slug: route.split('/')" + ) + .replace('remote/graphql-eslint', 'graphql-eslint') + .split('\n') + .slice(1, -1) + .join('\n')} +~~~`) + return +} + +# Remote Content + +> [!NOTE] +> +> You can check out the +> [SWR i18n example](https://github.com/shuding/nextra/blob/main/examples/swr-site/app/%5Blang%5D/remote/graphql-eslint/%5B%5B...slug%5D%5D/page.tsx) +> source code. + + + +## Create `[[...slug]]/page.tsx` file + +Create `[[...slug]]/page.tsx` file in `app/` directory with the following +content: + + + +## Enhance `pageMap` + +You need to modify `pageMap` list in `layout` file, to properly display sidebar +and mobile navigation. + +```tsx filename="app/layout.tsx" +import { getPageMap } from 'nextra/page-map' +import { pageMap as graphqlEslintPageMap } from './graphql-eslint/[[...slug]]/page' + +// ... + +const pageMap = [...(await getPageMap()), graphqlEslintPageMap] +``` + + diff --git a/docs/app/docs/advanced/table/page.mdx b/docs/app/docs/advanced/table/page.mdx new file mode 100644 index 0000000000..85df7c2fe8 --- /dev/null +++ b/docs/app/docs/advanced/table/page.mdx @@ -0,0 +1,67 @@ +--- +icon: TableIcon +--- + +# Rendering Tables + +This guide covers different ways to render tables in MDX, including GFM syntax +and literal HTML tag. + +## GFM syntax + +In Markdown, it is preferable to write tables via +[GFM syntax](https://github.github.com/gfm/#tables-extension-). + +```mdx filename="MDX" +| left | center | right | +| :----- | :----: | ----: | +| foo | bar | baz | +| banana | apple | kiwi | +``` + +will be rendered as: + +| left | center | right | +| :----- | :----: | ----: | +| foo | bar | baz | +| banana | apple | kiwi | + +## Literal HTML tables + +If you try to render table with literal HTML elements `{:js}`, +`{:js}`, `{:js}`, `{:js}`, `
{:js}` and `{:js}` – your +table will be unstyled because +[MDX](https://mdxjs.com/docs/using-mdx/#components) doesn't replace literal HTML +elements with components provided by `useMDXComponents(){:js}`[^1]. + +> [!TIP] +> +> Instead, use the [built-in `` component](/docs/guide/built-ins/table) +> available via `nextra/components`. + +## Changing default behaviour + +If you want to use standard HTML elements for your tables but have them styled +with components provided by `useMDXComponents(){:js}`[^1], you can do this by +configuring Nextra. + +To achieve this, pass the `whiteListTagsStyling` option to the Nextra function, +including an array of tags you want to replace. + +Here's an example configuration in your `next.config.mjs` file: + +```js filename="next.config.mjs" {4} +import nextra from 'nextra' + +const withNextra = nextra({ + whiteListTagsStyling: ['table', 'thead', 'tbody', 'tr', 'th', 'td'] +}) + +export default withNextra() +``` + +In this example, the tags `
`, ``, ``, ``, `
`, and +`` will be replaced with corresponding MDX components, allowing for +customized styling. + +[^1]: https://mdxjs.com/packages/react/#usemdxcomponentscomponents diff --git a/docs/app/docs/advanced/tailwind-css/page.mdx b/docs/app/docs/advanced/tailwind-css/page.mdx new file mode 100644 index 0000000000..7782e40ca2 --- /dev/null +++ b/docs/app/docs/advanced/tailwind-css/page.mdx @@ -0,0 +1,72 @@ +--- +icon: TailwindIcon +--- + +import { Steps } from 'nextra/components' + +# Tailwind CSS + +Tailwind CSS is a CSS framework that provides a set of pre-defined CSS classes +to quickly style elements. You can follow the official +[Tailwind CSS documentation for Next.js](https://tailwindcss.com/docs/guides/nextjs) +to set up Tailwind CSS for your Nextra project. + + + +## Create `tailwind.config.js` file + +To use Tailwind classes in your Markdown files, you will also need to add `.md` +and `.mdx` files to the `content` list in `tailwind.config.js`: + +```js filename="tailwind.config.js" /md,mdx/ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + './app/**/*.{js,jsx,ts,tsx,md,mdx}', + './content/**/*.{md,mdx}', + + // Or if using `src` directory: + './src/**/*.{js,jsx,ts,tsx,md,mdx}' + ], + theme: { + extend: {} + }, + plugins: [] +} +``` + +> [!TIP] +> +> You can also use a `tailwind.config.ts` file if you prefer TypeScript for your +> Tailwind CSS configuration. + +## Create the `globals.css` file + +Create a CSS file for Tailwind directives, `globals.css` for example: + +```css filename="globals.css" +@tailwind base; /* Apply Tailwind's base styles (Preflight) */ +@tailwind components; /* Include component styles */ +@tailwind utilities; /* Include utility classes */ +``` + +> [!NOTE] +> +> If you're using `nextra-theme-docs` or `nextra-theme-blog`, you don't need to +> include the `@tailwind base` directive. These themes already import Tailwind's +> preflight styles in their `style.css` files. + +## Import styles in the root layout + +To apply the styles globally, import the `globals.css` file in your root layout +file: + +```jsx filename="app/layout.jsx" +import '../path/to/your/globals.css' + +export default async function RootLayout({ children }) { + // ... Your layout logic here +} +``` + + diff --git a/docs/app/docs/advanced/twoslash/page.mdx b/docs/app/docs/advanced/twoslash/page.mdx new file mode 100644 index 0000000000..d156329e8e --- /dev/null +++ b/docs/app/docs/advanced/twoslash/page.mdx @@ -0,0 +1,74 @@ +# Twoslash Support + +Twoslash provides an inline type hove inside the code block. + +## Basic usage + +You can enable twoslash to your code blocks by adding a `twoslash` metadata: + +{/* prettier-ignore */} +````md copy=false filename="Markdown" +```ts twoslash +// @errors: 2540 +interface Todo { + title: string +} + +const todo: Readonly = { + title: 'Delete inactive users'.toUpperCase() + // ^? +} + +todo.title = 'Hello' + +Number.parseInt('123', 10) +// ^| + // Just comments, so Popup will be + // not behind the viewport of `` + // element due his `position: absolute` style + // +``` +```` + +Renders: + +{/* prettier-ignore */} +```ts twoslash +// @errors: 2540 +interface Todo { + title: string +} + +const todo: Readonly = { + title: 'Delete inactive users'.toUpperCase() + // ^? +} + +todo.title = 'Hello' + +Number.parseInt('123', 10) +// ^| + + + + +``` + +## Custom log message + +You can add log message to your code by adding: + +- `@log: ` Custom log message +- `@error: ` Custom error message +- `@warn: ` Custom warn message +- `@annotate: ` Custom annotate message + +```ts twoslash +// @log: Custom log message +const a = 1 +// @error: Custom error message +const b = 1 +// @warn: Custom warning message +const c = 1 +// @annotate: Custom annotation message +``` diff --git a/docs/app/docs/advanced/typescript/page.mdx b/docs/app/docs/advanced/typescript/page.mdx new file mode 100644 index 0000000000..d2f44f3642 --- /dev/null +++ b/docs/app/docs/advanced/typescript/page.mdx @@ -0,0 +1,50 @@ +--- +icon: TypeScriptIcon +--- + +import { Steps } from 'nextra/components' + +# TypeScript + +Nextra is built with TypeScript and provides excellent TypeScript support out of +the box. This guide will help you leverage TypeScript in your Nextra project. + +## Getting started + +To use TypeScript in your Nextra project, you need to: + + +### Install TypeScript and types packages as `devDependencies` + +```sh npm2yarn +npm i -D typescript @types/react @types/node +``` + +### `tsconfig.json` + +You can manually create a `tsconfig.json` file in the root of your project or +rename the extension of some of the existing files to `.ts` or `.tsx` and then +Next.js will detect TypeScript in your project and create a `tsconfig.json` file +for you. + + + +## Type definitions + +Nextra provides type definitions for distribution code for its components and +configurations. You can leverage these types by renaming your theme +configuration file to `.ts` or `.tsx` extension and importing a theme config +type, e.g. for `nextra-theme-docs`: + +```tsx filename="theme.config.tsx" +import type { DocsThemeConfig } from 'nextra-theme-docs' + +const config: DocsThemeConfig = { + // Your theme configuration +} +export default config +``` + +By leveraging TypeScript in your Nextra project, you can catch errors early, +improve code quality, and enhance the developer experience with better +autocompletion and type inference. diff --git a/docs/app/docs/blog-theme/get-posts-and-tags/page.mdx b/docs/app/docs/blog-theme/get-posts-and-tags/page.mdx new file mode 100644 index 0000000000..8792165737 --- /dev/null +++ b/docs/app/docs/blog-theme/get-posts-and-tags/page.mdx @@ -0,0 +1,16 @@ +--- +icon: FilesIcon +--- + +import { ExampleCode } from 'components/example-code' + +# Get Posts and Their Tags + +The following code snippet demonstrates how to retrieve all posts along with +their associated tags. + + diff --git a/docs/app/docs/blog-theme/page.mdx b/docs/app/docs/blog-theme/page.mdx new file mode 100644 index 0000000000..d72cfd712d --- /dev/null +++ b/docs/app/docs/blog-theme/page.mdx @@ -0,0 +1,29 @@ +--- +asIndexPage: true +sidebarTitle: Blog Theme +--- + +import { + ChevronRightIcon, + FilesIcon, + RSSIcon, + TagsIcon +} from '@components/icons' +import { Cards } from 'nextra/components' +import { FileIcon } from 'nextra/icons' +import { MDXRemote } from 'nextra/mdx-remote' +import { createIndexPage, getPageMap } from 'nextra/page-map' + +# Nextra Blog Theme + + diff --git a/docs/app/docs/blog-theme/posts/page.mdx b/docs/app/docs/blog-theme/posts/page.mdx new file mode 100644 index 0000000000..00b1eea18f --- /dev/null +++ b/docs/app/docs/blog-theme/posts/page.mdx @@ -0,0 +1,15 @@ +--- +icon: FileIcon +--- + +import { ExampleCode } from 'components/example-code' + +# Posts Page + +The following code snippet demonstrates how to create `/posts` page. + + diff --git a/docs/app/docs/blog-theme/rss/page.mdx b/docs/app/docs/blog-theme/rss/page.mdx new file mode 100644 index 0000000000..3fba006151 --- /dev/null +++ b/docs/app/docs/blog-theme/rss/page.mdx @@ -0,0 +1,15 @@ +--- +icon: RSSIcon +--- + +import { ExampleCode } from 'components/example-code' + +# Generate RSS feed + +The following code snippet demonstrates how to create `/rss.xml` route. + + diff --git a/docs/app/docs/blog-theme/start/page.mdx b/docs/app/docs/blog-theme/start/page.mdx new file mode 100644 index 0000000000..a8e8a3f2f4 --- /dev/null +++ b/docs/app/docs/blog-theme/start/page.mdx @@ -0,0 +1,81 @@ +--- +icon: ChevronRightIcon +--- + +import InstallNextraTheme from '@components/install-nextra-theme.mdx' +import ReadyToGo from '@components/ready-to-go.mdx' +import { ExampleCode } from '@components/example-code' +import { Steps } from 'nextra/components' + +# Get Started + +> [!NOTE] +> +> An example of the blog theme can be found [here](https://demo.vercel.blog), +> with source code [here](https://github.com/vercel/nextjs-portfolio-starter). + +Similar to the [Docs Theme](/docs/docs-theme/start), you can install the blog +theme with the following commands: + +## Start as a new project + + +### Install + +To create a Nextra Blog site manually, you have to install **Next.js**, +**React**, **Nextra**, and **Nextra Blog Theme**. In your project directory, run +the following command to install the dependencies: + +```sh npm2yarn +npm i next react react-dom nextra nextra-theme-blog +``` + +> [!NOTE] +> +> If you already have Next.js installed in your project, you only need to +> install `nextra` and `nextra-theme-blog` as the add-ons. + + + +### Add [`mdx-components` file](/docs/file-conventions/mdx-components-file) + +### Create root layout + + + +{/* + +### Create Blog Theme Config + +Lastly, create the corresponding `theme.config.jsx` file in your project's root +directory. This will be used to configure the Nextra Blog theme: + +```jsx filename="theme.config.jsx" +export default { + footer:

MIT 2023 © Nextra.

, + head: ({ title, meta }) => ( + <> + {meta.description && ( + + )} + {meta.tag && } + {meta.author && } + + ), + readMore: 'Read More →', + postFooter: null, + darkMode: false, + navs: [ + { + url: 'https://github.com/shuding/nextra', + name: 'Nextra' + } + ] +} +``` + +*/} + + + +
diff --git a/docs/app/docs/blog-theme/tags/page.mdx b/docs/app/docs/blog-theme/tags/page.mdx new file mode 100644 index 0000000000..d4deea57e6 --- /dev/null +++ b/docs/app/docs/blog-theme/tags/page.mdx @@ -0,0 +1,15 @@ +--- +icon: TagsIcon +--- + +import { ExampleCode } from 'components/example-code' + +# Tags Page + +The following code snippet demonstrates how to create `/tags/:id` pages. + + diff --git a/docs/app/docs/built-ins/banner/page.mdx b/docs/app/docs/built-ins/banner/page.mdx new file mode 100644 index 0000000000..62e1fe8b68 --- /dev/null +++ b/docs/app/docs/built-ins/banner/page.mdx @@ -0,0 +1,44 @@ +--- +sidebarTitle: Banner +--- + +# `` Component + +Show a banner on the top of the website. It can be used to show a warning or a +notice. + +| | | | | +| ------------- | ---------------- | ---------------------- | ------------------------------------- | +| `dismissible` | `boolean{:ts}` | `true` | Closable banner or not. | +| `key` | `string{:ts}` | `'nextra-banner'{:ts}` | Storage key to keep the banner state. | +| `children` | `ReactNode{:ts}` | | Content of the banner. | + +## Banner key + +A banner can be dismissed. By default, it's used by +[localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) +to keep the banner state on the client. + +If you have updated your banner text, you should change the key to make sure the +banner is shown again. The best practice is to always use a descriptive key for +the current text, for example: + +![Banner](/assets/docs/banner.png) + +```jsx filename="app/layout.jsx" {7-11} +import { Layout } from 'my-nextra-theme' +import { Banner } from 'nextra/components' + +export default function MyLayout({ children, ...props }) { + return ( + + + + 🎉 Nextra 2.0 is released. Read more → + + + {children} + + ) +} +``` diff --git a/docs/app/docs/built-ins/bleed/page.mdx b/docs/app/docs/built-ins/bleed/page.mdx new file mode 100644 index 0000000000..9ea738cecf --- /dev/null +++ b/docs/app/docs/built-ins/bleed/page.mdx @@ -0,0 +1,58 @@ +--- +sidebarTitle: Bleed +--- + +import { Bleed } from 'nextra/components' + +# `` Component + +## Example + +When wrapping your content with ``, it will be slightly wider than the +container and will overflow on both sides. + + +
+ _There is nothing to writing. All you do is sit down at a typewriter and **bleed**._ + + — Ernest Hemingway + +
+
+ +It provides a better reading experience when you want to present some graphical +information, which normally looks nicer in a larger size. + +For example, you can put text, image, video or any component inside: + + +