aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages
diff options
context:
space:
mode:
authorNtskwK <natsukawa247@outlook.com>2026-01-21 11:03:03 +0800
committerBegonia, HE <163421589+BegoniaHe@users.noreply.github.com>2026-01-29 02:55:10 +0100
commit4c92bc58fe6a217de029e52634c91160ed69fca3 (patch)
treec3aeb29591e5e2b952e506b802eb231237af88c5 /packages
parent0dc78f169678ddd0cbbdbd8d30e9bd7166ba4d87 (diff)
downloadDropOut-4c92bc58fe6a217de029e52634c91160ed69fca3.tar.gz
DropOut-4c92bc58fe6a217de029e52634c91160ed69fca3.zip
feat(docs): add React Router docs with Fumadocs
Diffstat (limited to 'packages')
-rw-r--r--packages/docs/.gitignore7
-rw-r--r--packages/docs/README.md14
-rw-r--r--packages/docs/app/app.css3
-rw-r--r--packages/docs/app/docs/page.tsx51
-rw-r--r--packages/docs/app/docs/search.ts12
-rw-r--r--packages/docs/app/lib/layout.shared.tsx9
-rw-r--r--packages/docs/app/lib/source.ts7
-rw-r--r--packages/docs/app/root.tsx73
-rw-r--r--packages/docs/app/routes.ts7
-rw-r--r--packages/docs/app/routes/home.tsx30
-rw-r--r--packages/docs/biome.json37
-rw-r--r--packages/docs/content/docs/index.mdx32
-rw-r--r--packages/docs/content/docs/meta.json3
-rw-r--r--packages/docs/content/docs/test.mdx24
-rw-r--r--packages/docs/package.json39
-rw-r--r--packages/docs/public/favicon.icobin0 -> 15086 bytes
-rw-r--r--packages/docs/react-router.config.ts23
-rw-r--r--packages/docs/source.config.ts7
-rw-r--r--packages/docs/tsconfig.json23
-rw-r--r--packages/docs/vite.config.ts17
20 files changed, 418 insertions, 0 deletions
diff --git a/packages/docs/.gitignore b/packages/docs/.gitignore
new file mode 100644
index 0000000..8fa7200
--- /dev/null
+++ b/packages/docs/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+/node_modules/
+
+# React Router
+/.react-router/
+/build/
+.source
diff --git a/packages/docs/README.md b/packages/docs/README.md
new file mode 100644
index 0000000..2b4c09a
--- /dev/null
+++ b/packages/docs/README.md
@@ -0,0 +1,14 @@
+# docs
+
+This is a React Router application generated with
+[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
+
+Run development server:
+
+```bash
+npm run dev
+# or
+pnpm dev
+# or
+yarn dev
+```
diff --git a/packages/docs/app/app.css b/packages/docs/app/app.css
new file mode 100644
index 0000000..50b3bc2
--- /dev/null
+++ b/packages/docs/app/app.css
@@ -0,0 +1,3 @@
+@import 'tailwindcss';
+@import 'fumadocs-ui/css/neutral.css';
+@import 'fumadocs-ui/css/preset.css';
diff --git a/packages/docs/app/docs/page.tsx b/packages/docs/app/docs/page.tsx
new file mode 100644
index 0000000..2618989
--- /dev/null
+++ b/packages/docs/app/docs/page.tsx
@@ -0,0 +1,51 @@
+import type { Route } from './+types/page';
+import { DocsLayout } from 'fumadocs-ui/layouts/docs';
+import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
+import { source } from '@/lib/source';
+import defaultMdxComponents from 'fumadocs-ui/mdx';
+import browserCollections from 'fumadocs-mdx:collections/browser';
+import { baseOptions } from '@/lib/layout.shared';
+import { useFumadocsLoader } from 'fumadocs-core/source/client';
+
+export async function loader({ params }: Route.LoaderArgs) {
+ const slugs = params['*'].split('/').filter((v) => v.length > 0);
+ const page = source.getPage(slugs);
+ if (!page) throw new Response('Not found', { status: 404 });
+
+ return {
+ path: page.path,
+ pageTree: await source.serializePageTree(source.getPageTree()),
+ };
+}
+
+const clientLoader = browserCollections.docs.createClientLoader({
+ component(
+ { toc, frontmatter, default: Mdx },
+ // you can define props for the `<Content />` component
+ props?: {
+ className?: string;
+ },
+ ) {
+ return (
+ <DocsPage toc={toc} {...props}>
+ <title>{frontmatter.title}</title>
+ <meta name="description" content={frontmatter.description} />
+ <DocsTitle>{frontmatter.title}</DocsTitle>
+ <DocsDescription>{frontmatter.description}</DocsDescription>
+ <DocsBody>
+ <Mdx components={{ ...defaultMdxComponents }} />
+ </DocsBody>
+ </DocsPage>
+ );
+ },
+});
+
+export default function Page({ loaderData }: Route.ComponentProps) {
+ const { path, pageTree } = useFumadocsLoader(loaderData);
+
+ return (
+ <DocsLayout {...baseOptions()} tree={pageTree}>
+ {clientLoader.useContent(path)}
+ </DocsLayout>
+ );
+}
diff --git a/packages/docs/app/docs/search.ts b/packages/docs/app/docs/search.ts
new file mode 100644
index 0000000..9603c72
--- /dev/null
+++ b/packages/docs/app/docs/search.ts
@@ -0,0 +1,12 @@
+import type { Route } from './+types/search';
+import { createFromSource } from 'fumadocs-core/search/server';
+import { source } from '@/lib/source';
+
+const server = createFromSource(source, {
+ // https://docs.orama.com/docs/orama-js/supported-languages
+ language: 'english',
+});
+
+export async function loader({ request }: Route.LoaderArgs) {
+ return server.GET(request);
+}
diff --git a/packages/docs/app/lib/layout.shared.tsx b/packages/docs/app/lib/layout.shared.tsx
new file mode 100644
index 0000000..af2b6f0
--- /dev/null
+++ b/packages/docs/app/lib/layout.shared.tsx
@@ -0,0 +1,9 @@
+import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
+
+export function baseOptions(): BaseLayoutProps {
+ return {
+ nav: {
+ title: 'React Router',
+ },
+ };
+}
diff --git a/packages/docs/app/lib/source.ts b/packages/docs/app/lib/source.ts
new file mode 100644
index 0000000..97cf767
--- /dev/null
+++ b/packages/docs/app/lib/source.ts
@@ -0,0 +1,7 @@
+import { loader } from 'fumadocs-core/source';
+import { docs } from 'fumadocs-mdx:collections/server';
+
+export const source = loader({
+ source: docs.toFumadocsSource(),
+ baseUrl: '/docs',
+});
diff --git a/packages/docs/app/root.tsx b/packages/docs/app/root.tsx
new file mode 100644
index 0000000..08b8aa8
--- /dev/null
+++ b/packages/docs/app/root.tsx
@@ -0,0 +1,73 @@
+import {
+ isRouteErrorResponse,
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+} from 'react-router';
+import { RootProvider } from 'fumadocs-ui/provider/react-router';
+import type { Route } from './+types/root';
+import './app.css';
+
+export const links: Route.LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+ <html lang="en" suppressHydrationWarning>
+ <head>
+ <meta charSet="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <Meta />
+ <Links />
+ </head>
+ <body className="flex flex-col min-h-screen">
+ <RootProvider>{children}</RootProvider>
+ <ScrollRestoration />
+ <Scripts />
+ </body>
+ </html>
+ );
+}
+
+export default function App() {
+ return <Outlet />;
+}
+
+export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
+ let message = 'Oops!';
+ let details = 'An unexpected error occurred.';
+ let stack: string | undefined;
+
+ if (isRouteErrorResponse(error)) {
+ message = error.status === 404 ? '404' : 'Error';
+ details =
+ error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
+ } else if (import.meta.env.DEV && error && error instanceof Error) {
+ details = error.message;
+ stack = error.stack;
+ }
+
+ return (
+ <main className="pt-16 p-4 w-full max-w-[1400px] mx-auto">
+ <h1>{message}</h1>
+ <p>{details}</p>
+ {stack && (
+ <pre className="w-full p-4 overflow-x-auto">
+ <code>{stack}</code>
+ </pre>
+ )}
+ </main>
+ );
+}
diff --git a/packages/docs/app/routes.ts b/packages/docs/app/routes.ts
new file mode 100644
index 0000000..60dd630
--- /dev/null
+++ b/packages/docs/app/routes.ts
@@ -0,0 +1,7 @@
+import { index, route, type RouteConfig } from '@react-router/dev/routes';
+
+export default [
+ index('routes/home.tsx'),
+ route('docs/*', 'docs/page.tsx'),
+ route('api/search', 'docs/search.ts'),
+] satisfies RouteConfig;
diff --git a/packages/docs/app/routes/home.tsx b/packages/docs/app/routes/home.tsx
new file mode 100644
index 0000000..7f03ba9
--- /dev/null
+++ b/packages/docs/app/routes/home.tsx
@@ -0,0 +1,30 @@
+import type { Route } from './+types/home';
+import { HomeLayout } from 'fumadocs-ui/layouts/home';
+import { Link } from 'react-router';
+import { baseOptions } from '@/lib/layout.shared';
+
+export function meta({}: Route.MetaArgs) {
+ return [
+ { title: 'New React Router App' },
+ { name: 'description', content: 'Welcome to React Router!' },
+ ];
+}
+
+export default function Home() {
+ return (
+ <HomeLayout {...baseOptions()}>
+ <div className="p-4 flex flex-col items-center justify-center text-center flex-1">
+ <h1 className="text-xl font-bold mb-2">Fumadocs on React Router.</h1>
+ <p className="text-fd-muted-foreground mb-4">
+ The truly flexible docs framework on React.js.
+ </p>
+ <Link
+ className="text-sm bg-fd-primary text-fd-primary-foreground rounded-full font-medium px-4 py-2.5"
+ to="/docs"
+ >
+ Open Docs
+ </Link>
+ </div>
+ </HomeLayout>
+ );
+}
diff --git a/packages/docs/biome.json b/packages/docs/biome.json
new file mode 100644
index 0000000..a637e58
--- /dev/null
+++ b/packages/docs/biome.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignoreUnknown": true,
+ "includes": [
+ "**",
+ "!node_modules",
+ "!.source"
+ ]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true
+ },
+ "domains": {
+ "react": "recommended"
+ }
+ },
+ "assist": {
+ "actions": {
+ "source": {
+ "organizeImports": "on"
+ }
+ }
+ }
+}
diff --git a/packages/docs/content/docs/index.mdx b/packages/docs/content/docs/index.mdx
new file mode 100644
index 0000000..6c2e629
--- /dev/null
+++ b/packages/docs/content/docs/index.mdx
@@ -0,0 +1,32 @@
+---
+title: Hello World
+description: |
+ Your first `document`
+ You'll love it!
+---
+
+Hey there! Fumadocs is the docs framework that also works on React Router!
+
+## Heading
+
+Hello World
+
+<Cards>
+ <Card title="Learn more about React Router" href="https://reactrouter.com" />
+ <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
+</Cards>
+
+```ts
+console.log('I love React!');
+```
+
+### Heading
+
+#### Heading
+
+| Head | Description |
+| ------------------------------- | ----------------------------------- |
+| `hello` | Hello World |
+| very **important** | Hey |
+| _Surprisingly_ | Fumadocs |
+| very long text that looks weird | hello world hello world hello world |
diff --git a/packages/docs/content/docs/meta.json b/packages/docs/content/docs/meta.json
new file mode 100644
index 0000000..bc00362
--- /dev/null
+++ b/packages/docs/content/docs/meta.json
@@ -0,0 +1,3 @@
+{
+ "pages": ["index", "..."]
+}
diff --git a/packages/docs/content/docs/test.mdx b/packages/docs/content/docs/test.mdx
new file mode 100644
index 0000000..cac4d26
--- /dev/null
+++ b/packages/docs/content/docs/test.mdx
@@ -0,0 +1,24 @@
+---
+title: Test
+description: A document to test Fumadocs
+---
+
+Hey there!
+
+## Cards
+
+<Cards>
+ <Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
+ <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
+</Cards>
+
+### CodeBlock
+
+```js
+console.log('Hello World');
+```
+
+#### List
+
+- Hello
+- World
diff --git a/packages/docs/package.json b/packages/docs/package.json
new file mode 100644
index 0000000..18a5bf3
--- /dev/null
+++ b/packages/docs/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@dropout/docs",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "react-router build",
+ "dev": "react-router dev",
+ "start": "react-router-serve ./build/server/index.js",
+ "types:check": "react-router typegen && fumadocs-mdx && tsc --noEmit",
+ "postinstall": "fumadocs-mdx",
+ "lint": "biome check",
+ "format": "biome format --write"
+ },
+ "dependencies": {
+ "@react-router/node": "^7.12.0",
+ "@react-router/serve": "^7.12.0",
+ "fumadocs-core": "16.4.7",
+ "fumadocs-mdx": "14.2.6",
+ "fumadocs-ui": "16.4.7",
+ "isbot": "^5.1.32",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "react-router": "^7.12.0"
+ },
+ "devDependencies": {
+ "@react-router/dev": "^7.12.0",
+ "@tailwindcss/vite": "^4.1.18",
+ "@types/mdx": "^2.0.13",
+ "@types/node": "^25.0.5",
+ "@types/react": "^19.2.8",
+ "@types/react-dom": "^19.2.3",
+ "react-router-devtools": "^6.1.0",
+ "tailwindcss": "^4.1.18",
+ "typescript": "^5.9.3",
+ "vite": "^7.3.1",
+ "vite-tsconfig-paths": "^6.0.4",
+ "@biomejs/biome": "^2.3.11"
+ }
+}
diff --git a/packages/docs/public/favicon.ico b/packages/docs/public/favicon.ico
new file mode 100644
index 0000000..5dbdfcd
--- /dev/null
+++ b/packages/docs/public/favicon.ico
Binary files differ
diff --git a/packages/docs/react-router.config.ts b/packages/docs/react-router.config.ts
new file mode 100644
index 0000000..cfcfbe4
--- /dev/null
+++ b/packages/docs/react-router.config.ts
@@ -0,0 +1,23 @@
+import type { Config } from '@react-router/dev/config';
+import { glob } from 'node:fs/promises';
+import { createGetUrl, getSlugs } from 'fumadocs-core/source';
+
+const getUrl = createGetUrl('/docs');
+
+export default {
+ ssr: true,
+ async prerender({ getStaticPaths }) {
+ const paths: string[] = [];
+ const excluded: string[] = ['/api/search'];
+
+ for (const path of getStaticPaths()) {
+ if (!excluded.includes(path)) paths.push(path);
+ }
+
+ for await (const entry of glob('**/*.mdx', { cwd: 'content/docs' })) {
+ paths.push(getUrl(getSlugs(entry)));
+ }
+
+ return paths;
+ },
+} satisfies Config;
diff --git a/packages/docs/source.config.ts b/packages/docs/source.config.ts
new file mode 100644
index 0000000..0564068
--- /dev/null
+++ b/packages/docs/source.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
+
+export const docs = defineDocs({
+ dir: 'content/docs',
+});
+
+export default defineConfig();
diff --git a/packages/docs/tsconfig.json b/packages/docs/tsconfig.json
new file mode 100644
index 0000000..717253d
--- /dev/null
+++ b/packages/docs/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "types": ["node", "vite/client"],
+ "target": "esnext",
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "rootDirs": [".", "./.react-router/types"],
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./app/*"],
+ "fumadocs-mdx:collections/*": [".source/*"]
+ },
+ "esModuleInterop": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "strict": true
+ }
+}
diff --git a/packages/docs/vite.config.ts b/packages/docs/vite.config.ts
new file mode 100644
index 0000000..f408dc5
--- /dev/null
+++ b/packages/docs/vite.config.ts
@@ -0,0 +1,17 @@
+import { reactRouter } from '@react-router/dev/vite';
+import tailwindcss from '@tailwindcss/vite';
+import { defineConfig } from 'vite';
+import tsconfigPaths from 'vite-tsconfig-paths';
+import mdx from 'fumadocs-mdx/vite';
+import * as MdxConfig from './source.config';
+
+export default defineConfig({
+ plugins: [
+ mdx(MdxConfig),
+ tailwindcss(),
+ reactRouter(),
+ tsconfigPaths({
+ root: __dirname,
+ }),
+ ],
+});