chore: migrate to Next.js 15.5, using auto-generated types

This commit is contained in:
Fuma Nama
2025-08-20 14:16:16 +08:00
parent 10c27cb6c9
commit aee3d75684
54 changed files with 1212 additions and 964 deletions
@@ -4,7 +4,7 @@ import { cn } from '@/lib/cn';
import { buttonVariants } from '@/components/ui/button';
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
export function Control({ url }: { url: string }): React.ReactElement {
export function Control({ url }: { url: string }) {
const [isChecked, onCopy] = useCopyButton(() => {
void navigator.clipboard.writeText(`${window.location.origin}${url}`);
});
+6 -8
View File
@@ -9,9 +9,7 @@ import { Control } from '@/app/(home)/blog/[slug]/page.client';
import { getMDXComponents } from '@/mdx-components';
import path from 'node:path';
export default async function Page(props: {
params: Promise<{ slug: string }>;
}) {
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const params = await props.params;
const page = blog.getPage([params.slug]);
@@ -21,7 +19,7 @@ export default async function Page(props: {
return (
<>
<div
className="container rounded-xl mt-12 py-12 md:px-8"
className="mx-auto w-full max-w-fd-container rounded-xl mt-12 px-4 py-12 md:px-8"
style={{
backgroundColor: 'black',
backgroundImage: [
@@ -43,7 +41,7 @@ export default async function Page(props: {
Back
</Link>
</div>
<article className="container flex flex-col px-0 py-8 lg:flex-row lg:px-4">
<article className="flex flex-col mx-auto w-full max-w-fd-container py-8 lg:flex-row">
<div className="prose min-w-0 flex-1 p-4">
<InlineTOC items={toc} />
<Mdx components={getMDXComponents()} />
@@ -69,9 +67,9 @@ export default async function Page(props: {
);
}
export async function generateMetadata(props: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
export async function generateMetadata(
props: PageProps<'/blog/[slug]'>,
): Promise<Metadata> {
const params = await props.params;
const page = blog.getPage([params.slug]);
+2 -2
View File
@@ -1,7 +1,7 @@
import Link from 'next/link';
import { blog } from '@/lib/source';
export default function Page(): React.ReactElement {
export default function Page() {
const posts = [...blog.getPages()].sort(
(a, b) =>
new Date(b.data.date ?? b.file.name).getTime() -
@@ -21,7 +21,7 @@ export default function Page(): React.ReactElement {
</svg>`;
return (
<main className="container max-sm:px-0 md:py-12">
<main className="mx-auto w-full max-w-fd-container sm:px-4 md:py-12">
<div
className="h-[300px] p-8 md:h-[400px] md:p-12"
style={{
+1 -1
View File
@@ -5,7 +5,7 @@ import { buttonVariants } from '@/components/ui/button';
import { cn } from '@/lib/cn';
import Spot from '@/public/spot.png';
export default function DocsPage(): React.ReactElement {
export default function DocsPage() {
return (
<main className="container flex flex-col items-center py-16 text-center z-2">
<div className="absolute inset-0 z-[-1] overflow-hidden duration-1000 animate-in fade-in [perspective:2000px]">
+1 -2
View File
@@ -1,4 +1,3 @@
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions, linkItems } from '@/lib/layout.shared';
import {
@@ -12,7 +11,7 @@ import Image from 'next/image';
import Preview from '@/public/banner.png';
import { Book, ComponentIcon, Pencil, PlusIcon, Server } from 'lucide-react';
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/'>) {
return (
<HomeLayout
{...baseOptions()}
+5 -12
View File
@@ -14,12 +14,7 @@ import {
PageTOCTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import {
type ComponentProps,
type FC,
type ReactElement,
type ReactNode,
} from 'react';
import { type ComponentProps, type FC, type ReactNode } from 'react';
import * as Twoslash from 'fumadocs-twoslash/ui';
import { Callout } from 'fumadocs-ui/components/callout';
import { TypeTable } from 'fumadocs-ui/components/type-table';
@@ -61,9 +56,7 @@ const generator = createGenerator();
export const revalidate = false;
export default async function Page(props: {
params: Promise<{ slug: string[] }>;
}): Promise<ReactElement> {
export default async function Page(props: PageProps<'/docs/[...slug]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
@@ -183,9 +176,9 @@ function DocsCategory({ url }: { url: string }) {
);
}
export async function generateMetadata(props: {
params: Promise<{ slug: string[] }>;
}): Promise<Metadata> {
export async function generateMetadata(
props: PageProps<'/docs/[...slug]'>,
): Promise<Metadata> {
const { slug = [] } = await props.params;
const page = source.getPage(slug);
if (!page) notFound();
+1 -2
View File
@@ -1,5 +1,4 @@
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions, linkItems, logo } from '@/lib/layout.shared';
import { source } from '@/lib/source';
import { LargeSearchToggle } from 'fumadocs-ui/components/layout/search-toggle';
@@ -9,7 +8,7 @@ import { cn } from '@/lib/cn';
import { buttonVariants } from '@/components/ui/button';
import 'katex/dist/katex.min.css';
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/docs'>) {
const base = baseOptions();
return (
+1 -1
View File
@@ -7,7 +7,7 @@ export const revalidate = false;
export async function GET(
_req: NextRequest,
{ params }: { params: Promise<{ slug: string[] }> },
{ params }: RouteContext<'/llms.mdx/[...slug]'>,
) {
const slug = (await params).slug;
const page = source.getPage(slug);
+1 -1
View File
@@ -8,7 +8,7 @@ const fontBold = readFileSync('./app/og/[...slug]/JetBrainsMono-Bold.ttf');
export async function GET(
_req: Request,
{ params }: { params: Promise<{ slug: string[] }> },
{ params }: RouteContext<'/og/[...slug]'>,
) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
@@ -30,6 +30,12 @@ description: Auto-generated type table
</Wrapper>
<Callout title="Server Component only" type="warn">
You cannot use this in a client component.
</Callout>
It generates a table for your docs based on TypeScript definitions.
## Usage
@@ -75,12 +81,6 @@ export interface MyInterface {
The path is relative to your project directory (`cwd`), because `AutoTypeTable` is a React Server Component, it cannot access build-time information like MDX file path.
<Callout title="Server Component only" type="warn">
You cannot use this in a client component.
</Callout>
### From Type
You can specify the type to generate, without an actual TypeScript file.
+3 -1
View File
@@ -45,7 +45,9 @@ A command line tool to install UI components and automate things, useful for cus
### Terminology
**Markdown/MDX:** Markdown is a markup language for creating formatted text. Fumadocs supports Markdown and MDX (superset of Markdown) out-of-the-box.
**Markdown/MDX:** Markdown is a markup language for creating formatted text. Fumadocs natively supports Markdown and MDX (superset of Markdown).
**[Bun](https://bun.sh):** A JavaScript runtime, we use it for running scripts.
Although not required, some basic knowledge of React.js would be useful for further customisations.
+14 -14
View File
@@ -12,18 +12,18 @@
"start": "next start"
},
"dependencies": {
"@ai-sdk/openai-compatible": "^1.0.7",
"@ai-sdk/react": "^2.0.15",
"@ai-sdk/openai-compatible": "^1.0.10",
"@ai-sdk/react": "^2.0.18",
"@fumadocs/mdx-remote": "workspace:*",
"@mixedbread/sdk": "^0.24.0",
"@mixedbread/sdk": "^0.25.0",
"@orama/orama": "^3.1.11",
"@oramacloud/client": "^2.1.4",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-tooltip": "^1.2.8",
"@shikijs/rehype": "^3.9.2",
"@shikijs/transformers": "^3.9.2",
"ai": "^5.0.15",
"@shikijs/rehype": "^3.11.0",
"@shikijs/transformers": "^3.11.0",
"ai": "^5.0.18",
"class-variance-authority": "^0.7.1",
"feed": "^5.1.0",
"fumadocs-core": "workspace:*",
@@ -35,12 +35,12 @@
"fumadocs-ui": "workspace:*",
"hast-util-to-jsx-runtime": "^2.3.6",
"katex": "^0.16.22",
"lucide-react": "^0.539.0",
"mermaid": "^11.9.0",
"next": "15.4.6",
"lucide-react": "^0.540.0",
"mermaid": "^11.10.0",
"next": "15.5.0",
"next-themes": "^0.4.6",
"octokit": "^5.0.3",
"oxc-transform": "^0.82.1",
"oxc-transform": "^0.82.2",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"rehype-katex": "^7.0.1",
@@ -50,7 +50,7 @@
"remark-mdx": "^3.1.0",
"remark-rehype": "^11.1.2",
"scroll-into-view-if-needed": "^3.1.0",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"tailwind-merge": "^3.3.1",
"twoslash": "^0.3.4",
"unist-util-visit": "^5.0.0",
@@ -58,9 +58,9 @@
},
"devDependencies": {
"@fumadocs/cli": "workspace:*",
"@next/bundle-analyzer": "15.4.6",
"@next/env": "15.4.6",
"@next/eslint-plugin-next": "15.4.6",
"@next/bundle-analyzer": "15.5.0",
"@next/env": "15.5.0",
"@next/eslint-plugin-next": "15.5.0",
"@tailwindcss/postcss": "^4.1.12",
"@types/hast": "^3.0.4",
"@types/mdx": "^2.0.13",
+1 -1
View File
@@ -11,7 +11,7 @@
"@fumadocs/content-collections": "workspace:*",
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "^15.4.6",
"next": "^15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1"
},
+1 -5
View File
@@ -1,14 +1,10 @@
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';
export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
}: LayoutProps<'/[lang]'>) {
const { lang } = await params;
return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>;
@@ -10,9 +10,9 @@ import { metadataImage } from '@/lib/metadata';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
export default async function Page(props: {
params: Promise<{ lang: string; slug?: string[] }>;
}) {
export default async function Page(
props: PageProps<'/[lang]/docs/[[...slug]]'>,
) {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();
@@ -34,9 +34,9 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ lang: string; slug?: string[] }>;
}): Promise<Metadata> {
export async function generateMetadata(
props: PageProps<'/[lang]/docs/[[...slug]]'>,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();
+1 -5
View File
@@ -1,4 +1,3 @@
import type { ReactNode } from 'react';
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';
@@ -6,10 +5,7 @@ import { baseOptions } from '@/lib/layout.shared';
export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
}: LayoutProps<'/[lang]/docs'>) {
const { lang } = await params;
return (
+1 -5
View File
@@ -1,7 +1,6 @@
import 'fumadocs-ui/style.css';
import { RootProvider } from 'fumadocs-ui/provider';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
import { defineI18nUI } from 'fumadocs-ui/i18n';
import { i18n } from '@/lib/i18n';
@@ -30,10 +29,7 @@ const { provider } = defineI18nUI(i18n, {
export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
}: LayoutProps<'/[lang]'>) {
const { lang } = await params;
return (
<html lang={lang} className={inter.className} suppressHydrationWarning>
+1 -1
View File
@@ -12,7 +12,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1"
},
+1 -6
View File
@@ -1,11 +1,6 @@
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';
export default function Layout({
children,
}: {
children: ReactNode;
}): React.ReactElement {
export default function Layout({ children }: LayoutProps<'/'>) {
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
}
@@ -4,7 +4,7 @@ import { notFound } from 'next/navigation';
export async function GET(
_req: Request,
{ params }: { params: Promise<{ slug: string[] }> },
{ params }: RouteContext<'/docs-og/[...slug]'>,
) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
@@ -7,10 +7,9 @@ import {
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -32,9 +31,9 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
+1 -2
View File
@@ -1,9 +1,8 @@
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/docs'>) {
return (
<DocsLayout tree={source.pageTree} {...baseOptions()}>
{children}
+1 -2
View File
@@ -1,13 +1,12 @@
import { RootProvider } from 'fumadocs-ui/provider';
import './global.css';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
const inter = Inter({
subsets: ['latin'],
});
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/'>) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body
+1 -1
View File
@@ -11,7 +11,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1"
},
+2 -2
View File
@@ -14,10 +14,10 @@
"fumadocs-mdx": "workspace:*",
"fumadocs-openapi": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1",
"shiki": "^3.9.2"
"shiki": "^3.11.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.12",
+1 -1
View File
@@ -14,7 +14,7 @@
"fumadocs-mdx": "workspace:*",
"fumadocs-python": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
+2 -2
View File
@@ -15,7 +15,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"isbot": "^5.1.29",
"isbot": "^5.1.30",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.1"
@@ -29,7 +29,7 @@
"react-router-devtools": "^5.0.6",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vite": "^7.1.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
+6 -6
View File
@@ -11,13 +11,13 @@
"dependencies": {
"@react-router/node": "^7.8.1",
"@react-router/serve": "^7.8.1",
"@vitejs/plugin-react": "^5.0.0",
"@vitejs/plugin-rsc": "^0.4.20",
"@vitejs/plugin-react": "^5.0.1",
"@vitejs/plugin-rsc": "^0.4.21",
"cross-env": "^10.0.0",
"fumadocs-core": "^15.6.10",
"fumadocs-core": "^15.6.12",
"fumadocs-mdx": "^11.7.5",
"fumadocs-ui": "^15.6.10",
"isbot": "^5.1.29",
"fumadocs-ui": "^15.6.12",
"isbot": "^5.1.30",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.1"
@@ -31,7 +31,7 @@
"react-router-devtools": "^5.0.6",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vite": "^7.1.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
+2 -2
View File
@@ -14,7 +14,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"isbot": "^5.1.29",
"isbot": "^5.1.30",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.1"
@@ -28,7 +28,7 @@
"react-router-devtools": "^5.0.6",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vite": "^7.1.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
+1 -1
View File
@@ -11,7 +11,7 @@
"@fumadocs/mdx-remote": "workspace:*",
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1",
"tinyglobby": "^0.2.14"
+6 -6
View File
@@ -10,24 +10,24 @@
},
"dependencies": {
"@orama/tokenizers": "^3.1.11",
"@tanstack/react-router": "^1.131.17",
"@tanstack/react-router-devtools": "^1.131.17",
"@tanstack/react-start": "^1.131.17",
"@tanstack/react-router": "^1.131.27",
"@tanstack/react-router-devtools": "^1.131.27",
"@tanstack/react-start": "^1.131.27",
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"lucide-static": "^0.539.0",
"lucide-static": "^0.540.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"tailwind-merge": "^3.3.1",
"vite": "^7.1.2"
"vite": "^7.1.3"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.12",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"@vitejs/plugin-react": "^5.0.1",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite-tsconfig-paths": "^5.1.4"
+6 -6
View File
@@ -9,24 +9,24 @@
"start": "node .output/server/index.mjs"
},
"dependencies": {
"@tanstack/react-router": "^1.131.17",
"@tanstack/react-router-devtools": "^1.131.17",
"@tanstack/react-start": "^1.131.17",
"@tanstack/react-router": "^1.131.27",
"@tanstack/react-router-devtools": "^1.131.27",
"@tanstack/react-start": "^1.131.27",
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"lucide-static": "^0.539.0",
"lucide-static": "^0.540.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"tailwind-merge": "^3.3.1",
"vite": "^7.1.2"
"vite": "^7.1.3"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.12",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"@vitejs/plugin-react": "^5.0.1",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite-tsconfig-paths": "^5.1.4"
+1 -1
View File
@@ -27,6 +27,6 @@
"postcss": "^8.5.6",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite": "7.1.2"
"vite": "7.1.3"
}
}
+1 -1
View File
@@ -25,6 +25,6 @@
"postcss": "^8.5.6",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
"vite": "^7.1.2"
"vite": "^7.1.3"
}
}
+2 -2
View File
@@ -17,7 +17,7 @@
"version": "changeset version && pnpm install --lockfile-only"
},
"devDependencies": {
"@changesets/cli": "^2.29.5",
"@changesets/cli": "^2.29.6",
"@eslint/js": "^9.33.0",
"bun": "^1.2.20",
"concurrently": "^9.2.0",
@@ -31,7 +31,7 @@
"tsup": "8.5.0",
"turbo": "2.5.6",
"typescript": "^5.9.2",
"typescript-eslint": "^8.39.1",
"typescript-eslint": "^8.40.0",
"vitest": "^3.2.4"
},
"packageManager": "[email protected]",
+6 -6
View File
@@ -99,8 +99,8 @@
"dependencies": {
"@formatjs/intl-localematcher": "^0.6.1",
"@orama/orama": "^3.1.11",
"@shikijs/rehype": "^3.9.2",
"@shikijs/transformers": "^3.9.2",
"@shikijs/rehype": "^3.11.0",
"@shikijs/transformers": "^3.11.0",
"github-slugger": "^2.0.0",
"hast-util-to-estree": "^3.1.3",
"hast-util-to-jsx-runtime": "^2.3.6",
@@ -112,14 +112,14 @@
"remark-gfm": "^4.0.1",
"remark-rehype": "^11.1.2",
"scroll-into-view-if-needed": "^3.1.0",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@mdx-js/mdx": "^3.1.0",
"@mixedbread/sdk": "^0.24.0",
"@mixedbread/sdk": "^0.25.0",
"@oramacloud/client": "^2.1.4",
"@tanstack/react-router": "^1.131.17",
"@tanstack/react-router": "^1.131.27",
"@types/estree-jsx": "^1.0.5",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.3",
@@ -131,7 +131,7 @@
"eslint-config-custom": "workspace:*",
"mdast-util-mdx-jsx": "^3.2.0",
"mdast-util-mdxjs-esm": "^2.0.1",
"next": "^15.4.6",
"next": "^15.5.0",
"react-router": "^7.8.1",
"remark-mdx": "^3.1.0",
"remove-markdown": "^0.6.2",
+7 -7
View File
@@ -13,27 +13,27 @@
"@react-router/serve": "^7.8.1",
"@tailwindcss/postcss": "^4.1.12",
"@tailwindcss/vite": "^4.1.12",
"@tanstack/react-router": "^1.131.17",
"@tanstack/react-start": "^1.131.17",
"@tanstack/react-router": "^1.131.27",
"@tanstack/react-start": "^1.131.27",
"@types/mdx": "^2.0.13",
"@types/node": "24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"@vitejs/plugin-react": "^5.0.1",
"gray-matter": "^4.0.3",
"isbot": "^5.1.29",
"next": "15.4.6",
"isbot": "^5.1.30",
"next": "15.5.0",
"postcss": "^8.5.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.1",
"react-router-devtools": "^5.0.6",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"tailwindcss": "^4.1.12",
"tinyglobby": "^0.2.14",
"typescript": "^5.9.2",
"vinxi": "^0.5.8",
"vite": "^7.1.2",
"vite": "^7.1.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
@@ -1,18 +1,17 @@
import { source } from '@/lib/source';
import type { Metadata } from 'next';
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { MDXContent } from '@content-collections/mdx/react';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -38,9 +37,9 @@ export function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -1,18 +1,16 @@
import { source } from '@/lib/source';
import {
DocsPage,
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import { Metadata } from 'next';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -39,9 +37,9 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}): Promise<Metadata> {
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -6,7 +6,7 @@ import {
} from 'fumadocs-mdx/config';
// You can customise Zod schemas for frontmatter and `meta.json` here
// see https://fumadocs.vercel.app/docs/mdx/collections#define-docs
// see https://fumadocs.dev/docs/mdx/collections#define-docs
export const docs = defineDocs({
docs: {
schema: frontmatterSchema,
@@ -1,13 +1,12 @@
import '@/app/global.css';
import { RootProvider } from 'fumadocs-ui/provider';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
const inter = Inter({
subsets: ['latin'],
});
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/'>) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body className="flex flex-col min-h-screen">
@@ -1,7 +1,6 @@
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/'>) {
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
}
@@ -1,9 +1,8 @@
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';
import { source } from '@/lib/source';
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/docs'>) {
return (
<DocsLayout tree={source.pageTree} {...baseOptions()}>
{children}
@@ -1,13 +1,12 @@
import { RootProvider } from 'fumadocs-ui/provider';
import 'fumadocs-ui/style.css';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
const inter = Inter({
subsets: ['latin'],
});
export default function Layout({ children }: { children: ReactNode }) {
export default function Layout({ children }: LayoutProps<'/'>) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body
+1 -1
View File
@@ -40,7 +40,7 @@
"estree-util-to-js": "^2.0.0",
"estree-util-value-to-estree": "^3.4.0",
"npm-to-yarn": "^3.0.1",
"oxc-transform": "^0.82.1",
"oxc-transform": "^0.82.2",
"unist-util-visit": "^5.0.0",
"zod": "^4.0.17"
},
+4 -4
View File
@@ -86,14 +86,14 @@
"eslint-config-custom": "workspace:*",
"fumadocs-core": "workspace:*",
"mdast-util-mdx-jsx": "^3.2.0",
"next": "^15.4.6",
"next": "^15.5.0",
"react": "^19.1.1",
"rollup": "^4.46.2",
"rollup": "^4.46.3",
"tsconfig": "workspace:*",
"unified": "^11.0.5",
"vfile": "^6.0.3",
"vite": "^7.1.2",
"webpack": "^5.101.2"
"vite": "^7.1.3",
"webpack": "^5.101.3"
},
"peerDependencies": {
"@fumadocs/mdx-remote": "^1.4.0",
+1 -1
View File
@@ -70,7 +70,7 @@
"react-hook-form": "^7.62.0",
"remark": "^15.0.1",
"remark-rehype": "^11.1.2",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"tinyglobby": "^0.2.14",
"xml-js": "^1.6.11"
},
+1 -1
View File
@@ -45,7 +45,7 @@
"eslint-config-custom": "workspace:*",
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"lucide-react": "^0.539.0",
"lucide-react": "^0.540.0",
"tsconfig": "workspace:*"
},
"peerDependencies": {
+2 -2
View File
@@ -42,11 +42,11 @@
},
"dependencies": {
"@radix-ui/react-popover": "^1.1.15",
"@shikijs/twoslash": "^3.9.2",
"@shikijs/twoslash": "^3.11.0",
"mdast-util-from-markdown": "^2.0.2",
"mdast-util-gfm": "^3.1.0",
"mdast-util-to-hast": "^13.2.0",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"tailwind-merge": "^3.3.1",
"twoslash": "^0.3.4"
},
+1 -1
View File
@@ -40,7 +40,7 @@
"hast-util-to-jsx-runtime": "^2.3.6",
"remark": "^15.0.1",
"remark-rehype": "^11.1.2",
"shiki": "^3.9.2",
"shiki": "^3.11.0",
"tinyglobby": "^0.2.14",
"ts-morph": "^26.0.0",
"unist-util-visit": "^5.0.0"
+2 -2
View File
@@ -135,14 +135,14 @@
},
"devDependencies": {
"@fumadocs/cli": "workspace:*",
"@next/eslint-plugin-next": "^15.4.6",
"@next/eslint-plugin-next": "^15.5.0",
"@tailwindcss/cli": "^4.1.12",
"@types/lodash.merge": "^4.6.9",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"eslint-config-custom": "workspace:*",
"fumadocs-core": "workspace:*",
"next": "15.4.6",
"next": "15.5.0",
"tailwindcss": "^4.1.12",
"tsc-alias": "^1.8.16",
"tsconfig": "workspace:*"
+61
View File
@@ -0,0 +1,61 @@
'use client';
// TODO: remove next major
import type { I18nProviderProps } from '@/provider/base';
import { usePathname, useRouter } from 'fumadocs-core/framework';
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
import { useMemo } from 'react';
import { defaultTranslations, I18nContext } from '@/contexts/i18n';
/**
* @deprecated legacy I18n Provider, use `<RootProvider i18n={...} />` instead
*/
export function I18nProvider({
locales = [],
locale,
onChange: _onChange,
onLocaleChange = _onChange,
...props
}: I18nProviderProps & {
/**
* @deprecated use `onLocaleChange` instead
*/
onChange?: I18nProviderProps['onLocaleChange'];
}) {
const router = useRouter();
const pathname = usePathname();
const onChange = useEffectEvent((value: string) => {
if (onLocaleChange) {
return onLocaleChange(value);
}
const segments = pathname.split('/').filter((v) => v.length > 0);
// If locale prefix hidden
if (segments[0] !== locale) {
segments.unshift(value);
} else {
segments[0] = value;
}
router.push(`/${segments.join('/')}`);
router.refresh();
});
return (
<I18nContext.Provider
value={useMemo(
() => ({
locale,
locales,
text: {
...defaultTranslations,
...props.translations,
},
onChange,
}),
[locale, locales, onChange, props.translations],
)}
>
{props.children}
</I18nContext.Provider>
);
}
+2 -64
View File
@@ -1,72 +1,10 @@
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
import { useMemo } from 'react';
import {
defaultTranslations,
I18nContext,
type Translations,
} from '@/contexts/i18n';
import type { Translations } from '@/contexts/i18n';
import type { I18nProviderProps } from './provider/base';
import type { I18nConfig } from 'fumadocs-core/i18n';
export type { I18nProviderProps, Translations };
export { defaultTranslations } from './contexts/i18n';
// TODO: remove next major
/**
* @deprecated legacy I18n Provider, use `<RootProvider i18n={...} />` instead
*/
export function I18nProvider({
locales = [],
locale,
onChange: _onChange,
onLocaleChange = _onChange,
...props
}: I18nProviderProps & {
/**
* @deprecated use `onLocaleChange` instead
*/
onChange?: I18nProviderProps['onLocaleChange'];
}) {
const router = useRouter();
const pathname = usePathname();
const onChange = useEffectEvent((value: string) => {
if (onLocaleChange) {
return onLocaleChange(value);
}
const segments = pathname.split('/').filter((v) => v.length > 0);
// If locale prefix hidden
if (segments[0] !== locale) {
segments.unshift(value);
} else {
segments[0] = value;
}
router.push(`/${segments.join('/')}`);
router.refresh();
});
return (
<I18nContext.Provider
value={useMemo(
() => ({
locale,
locales,
text: {
...defaultTranslations,
...props.translations,
},
onChange,
}),
[locale, locales, onChange, props.translations],
)}
>
{props.children}
</I18nContext.Provider>
);
}
export { I18nProvider } from './contexts/legacy-i18n';
export function defineI18nUI<Languages extends string>(
config: I18nConfig<Languages>,
+1013 -728
View File
File diff suppressed because it is too large Load Diff