aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/docs/app
diff options
context:
space:
mode:
authorNtskwK <natsukawa247@outlook.com>2026-01-21 11:03:03 +0800
committerNtskwK <natsukawa247@outlook.com>2026-01-21 11:03:03 +0800
commitc792c62c86e4d9f4de8402610bb63000cd79bc3a (patch)
treec9efcc2e994d4242cd883317c4f8737d8a821600 /packages/docs/app
parentc281777d65c4207d2fc3805a52fbc6e1939f4909 (diff)
downloadDropOut-c792c62c86e4d9f4de8402610bb63000cd79bc3a.tar.gz
DropOut-c792c62c86e4d9f4de8402610bb63000cd79bc3a.zip
feat(docs): add React Router docs with Fumadocs
Diffstat (limited to 'packages/docs/app')
-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
8 files changed, 192 insertions, 0 deletions
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>
+ );
+}