Ariadocs

React Router

Edit on GitHub

Docs pages and an API reference with loaders.

These examples use React Router's framework mode.

Routes

app/routes.ts
import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
  index("routes/home.tsx"),
  route("docs/*", "routes/docs.tsx"),
  route("reference/:operation", "routes/reference.tsx"),
] satisfies RouteConfig;

Docs pages

The loader compiles the page on the server. MdxClient renders it.

app/routes/docs.tsx
import { useLoaderData } from "react-router";
import { isMdxNotFound } from "@ariadocs/mdx";
import { MdxClient } from "@ariadocs/mdx/client";
import type { Route } from "./+types/docs";
import { docs } from "../docs";

export async function loader({ params }: Route.LoaderArgs) {
  try {
    const { serialized, frontmatter } = await docs.serialize<{ title: string }>({ slug: params["*"] ?? "" });
    return { serialized, frontmatter };
  } catch (error) {
    if (isMdxNotFound(error)) throw new Response("Not found", { status: 404 });
    throw error;
  }
}

export default function DocsPage() {
  const { serialized, frontmatter } = useLoaderData<typeof loader>();
  return (
    <article>
      <h1>{frontmatter.title}</h1>
      <MdxClient serialized={serialized} />
    </article>
  );
}

API reference

app/routes/reference.tsx
import { useLoaderData } from "react-router";
import { OpenAPI } from "@ariadocs/components";
import type { Route } from "./+types/reference";
import { openapi } from "../openapi";

export async function loader({ params }: Route.LoaderArgs) {
  return { api: await openapi.parse(), operation: params.operation };
}

export default function ReferencePage() {
  const { api, operation } = useLoaderData<typeof loader>();
  return (
    <OpenAPI.Root api={api} operationBaseHref="/reference">
      <OpenAPI.Operation id={operation} headingLevel={1} />
    </OpenAPI.Root>
  );
}

Prerendering

List every page so they're built ahead of time:

react-router.config.ts
import type { Config } from "@react-router/dev/config";
import { docs } from "./app/docs";
import { openapi } from "./app/openapi";

export default {
  ssr: true,
  async prerender() {
    const docPaths = await docs.getPagePaths();
    const apiPaths = await openapi.getPagePaths();
    return [
      ...docPaths.map((path) => `/docs${path === "/" ? "" : path}`),
      ...apiPaths.map((path) => `/reference${path}`),
    ];
  },
} satisfies Config;