npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ariadocs/react

v2.1.0

Published

A documentation-building library for React and Next.js. Render MDX content, organize docs, and build modern documentation sites.

Readme

@ariadocs/react

A documentation-building library for React and Next.js. Render MDX content, organize docs with _meta.json, and build modern documentation sites with minimal setup.

🌐 Website: ariadocs.vercel.app

Installation

npm install @ariadocs/react
# or
pnpm add @ariadocs/react
# or
yarn add @ariadocs/react

Quick Start

1. Create a Docs Instance (Recommended)

Use createDocs() to avoid repeating configuration:

import { createDocs } from "@ariadocs/react";
import {
  remarkGfm,
  rehypePrism,
  rehypeSlug,
  rehypeAutolinkHeadings,
  rehypeCodeTitles,
} from "@ariadocs/react/plugins";

// Create once, use everywhere
export const docs = createDocs({
  contentDir: "contents/docs",
  rehypePlugins: [
    rehypeSlug,
    rehypeAutolinkHeadings,
    rehypeCodeTitles,
    rehypePrism,
  ],
  remarkPlugins: [remarkGfm],
});

2. Parse and Render MDX

import { docs } from "./docs";

export default async function DocPage({
  params,
}: {
  params: { path?: string[] };
}) {
  const slug = params.path?.join("/") ?? "index";

  // Just pass the slug - config is already set!
  const { MDX, frontmatter, toc } = await docs.parse({ slug });

  return (
    <article>
      <h1>{frontmatter.title}</h1>
      <nav>
        {toc.map((item) => (
          <a key={item.id} href={`#${item.id}`}>
            {item.value}
          </a>
        ))}
      </nav>
      {MDX}
    </article>
  );
}

3. Build Navigation Sidebar

import { docs, type NavItem } from "@ariadocs/react";

export default async function Sidebar() {
  // No need to pass contentDir - it's already configured!
  const navItems = await docs.getNavItems();
  return <NavList items={navItems} />;
}

function NavList({ items }: { items: NavItem[] }) {
  return (
    <ul>
      {items
        .filter((item) => item.nav)
        .map((item) => (
          <li key={item.href}>
            <a href={item.href}>{item.title}</a>
            {item.items.length > 0 && <NavList items={item.items} />}
          </li>
        ))}
    </ul>
  );
}

API Reference

createDocs(config) (Recommended)

Create a docs instance with pre-configured options. Eliminates repetitive configuration.

import { createDocs } from "@ariadocs/react";
import {
  remarkGfm,
  rehypePrism,
  rehypeSlug,
  rehypeAutolinkHeadings,
  rehypeCodeTitles,
} from "@ariadocs/react/plugins";

const docs = createDocs({
  contentDir: "contents/docs",
  remarkPlugins: [remarkGfm],
  rehypePlugins: [
    rehypeSlug,
    rehypeAutolinkHeadings,
    rehypeCodeTitles,
    rehypePrism,
  ],
  mdxComponents: {}, // Optional
});

// All methods share the config
await docs.parse({ slug: "intro" }); // Parse for server
await docs.serialize({ slug: "intro" }); // Serialize for client
await docs.getFrontmatter({ slug: "intro" }); // Get frontmatter only
await docs.getToc({ slug: "intro" }); // Get TOC only
await docs.readMdx({ slug: "intro" }); // Read raw content
await docs.getNavItems(); // Get navigation tree
await docs.getPagePaths(); // Get all page paths
docs.config; // Access the original config (readonly)

Override Options Per-Call

const docs = createDocs({
  contentDir: "contents/docs",
  rehypePlugins: defaultPlugins,
});

// Override plugins for a specific call
await docs.parse({
  slug: "special",
  rehypePlugins: customPlugins, // Uses customPlugins instead
});

Standalone Functions

Use these when you need one-off operations without creating an instance.

parseMdx(options)

Parse a local MDX file for server-side rendering.

const { raw, content, frontmatter, toc, MDX } = await parseMdx({
  contentDir: "contents/docs", // Required: path to content directory
  slug: "getting-started", // Required: file path without .mdx
  remarkPlugins: [], // Optional: remark plugins
  rehypePlugins: [], // Optional: rehype plugins
  mdxComponents: {}, // Optional: custom MDX components
});

serializeMdx(options)

Serialize MDX for client-side rendering with MdxClient.

// Server component
const { serialized, frontmatter, toc } = await serializeMdx({
  contentDir: "contents/docs",
  slug: "getting-started",
  rehypePlugins: [],
});

// Pass to client component
<DocClient serialized={serialized} />;

readMdx(options)

Read raw MDX file content.

const raw = await readMdx({
  contentDir: "contents/docs",
  slug: "getting-started",
});

getFrontmatter(options)

Get only the frontmatter from an MDX file.

const frontmatter = await getFrontmatter({
  contentDir: "contents/docs",
  slug: "getting-started",
});

getToc(options)

Get only the table of contents.

const toc = await getToc({
  contentDir: "contents/docs",
  slug: "getting-started",
});

Remote MDX Functions

For MDX content from APIs, CMS, or GitHub:

import {
  parseMdxRemote,
  serializeMdxRemote,
  getFrontmatterRemote,
  getTocRemote,
} from "@ariadocs/react";

// Get frontmatter from remote content
const frontmatter = await getFrontmatterRemote({ raw: mdxContentString });

// Get TOC from remote content
const toc = await getTocRemote({ raw: mdxContentString });

// Parse remote MDX
const { MDX, frontmatter, toc } = await parseMdxRemote({
  raw: mdxContentString,
  rehypePlugins: [],
});

// Serialize for client
const { serialized } = await serializeMdxRemote({
  raw: mdxContentString,
  rehypePlugins: [],
});

Navigation Functions

getNavItems(options)

Build navigation tree from content directory structure and _meta.json files.

const navItems = await getNavItems({
  contentDir: "contents/docs",
});

getPagePaths(options)

Get all page paths for static generation.

export async function generateStaticParams() {
  const paths = await getPagePaths({ contentDir: "contents/docs" });
  return paths.map((path) => ({ path: path.split("/").filter(Boolean) }));
}

slugToTitle(slug)

Convert a slug string to a title.

slugToTitle("getting-started"); // "Getting Started"
slugToTitle("api-reference"); // "Api Reference"

Components

<MdxServer />

Server component for rendering MDX.

import { MdxServer } from "@ariadocs/react";
// or
import { MdxServer } from "@ariadocs/react/server";

<MdxServer
  raw={mdxContent}
  options={{
    remarkPlugins: [],
    rehypePlugins: [],
  }}
  components={
    {
      // Custom components
    }
  }
/>;

<MdxClient />

Client component for rendering serialized MDX.

"use client";
import { MdxClient } from "@ariadocs/react";
// or
import { MdxClient } from "@ariadocs/react/client";

<MdxClient serialized={serializedResult} mdxComponents={{}} />;

Plugins

Import individual plugins from the sub-path export:

import {
  remarkGfm,
  rehypePrism,
  rehypeAutolinkHeadings,
  rehypeSlug,
  rehypeCodeTitles,
  rehypeCodeRaw,
} from "@ariadocs/react/plugins";

| Plugin | Description | | ------------------------ | ----------------------------------------------- | | remarkGfm | GitHub Flavored Markdown support | | rehypePrism | Syntax highlighting with Prism.js | | rehypeSlug | Add IDs to headings | | rehypeAutolinkHeadings | Add anchor links to headings | | rehypeCodeTitles | Add titles to code blocks | | rehypeCodeRaw | Inject raw code string into <pre> for copying |

Recommended Setup

import { createDocs } from "@ariadocs/react";
import {
  remarkGfm,
  rehypePrism,
  rehypeSlug,
  rehypeAutolinkHeadings,
  rehypeCodeTitles,
} from "@ariadocs/react/plugins";

const docs = createDocs({
  contentDir: "contents/docs",
  remarkPlugins: [remarkGfm],
  rehypePlugins: [
    rehypeSlug,
    rehypeAutolinkHeadings,
    rehypeCodeTitles,
    rehypePrism,
  ],
});

Sub-path Exports

The package provides multiple entry points for tree-shaking:

| Export | Description | | ---------------------------- | ----------------------------- | | @ariadocs/react | Main entry (all exports) | | @ariadocs/react/client | MdxClient component | | @ariadocs/react/server | MdxServer component | | @ariadocs/react/plugins | All rehype/remark plugins | | @ariadocs/react/nav | Navigation functions | | @ariadocs/react/parse | Parse/serialize functions | | @ariadocs/react/types | Type definitions only | | @ariadocs/react/syntax.css | Prism syntax highlighting CSS |

Types

All types are exported from the main entry:

import type {
  // Core types
  BaseFrontmatter,
  DocsConfig,
  RemoteDocsConfig,
  ParseResult,
  SerializeResult,
  DocsInstance,
  ParseSlugOptions,
  LocalOptions,
  RemoteOptions,
  // Navigation types
  NavItem,
  MetaItem,
  // Plugin types
  RemarkPlugins,
  RehypePlugins,
  // Component types
  MdxServerProps,
  MdxClientProps,
  // Re-exports from external libs
  MDXComponents,
  TocItem,
} from "@ariadocs/react";

Organizing Content with _meta.json

Control navigation order, titles, and visibility with _meta.json files:

contents/docs/
├── _meta.json
├── introduction.mdx
├── getting-started.mdx
├── advanced/
│   ├── _meta.json
│   ├── configuration.mdx
│   └── plugins.mdx
└── api/
    └── reference.mdx

_meta.json Format

[
  { "slug": "introduction", "title": "Introduction", "nav": true },
  { "slug": "getting-started", "title": "Quick Start", "nav": true },
  { "slug": "advanced", "title": "Advanced", "nav": true },
  { "slug": "api", "title": "API Reference", "nav": true }
]

Options

| Property | Type | Default | Description | | -------- | ------------------------ | ------------------- | ------------------------------------ | | slug | string | Required | File or folder name (without .mdx) | | title | string | slugToTitle(slug) | Display title | | nav | boolean | true | Show in navigation | | props | Record<string, string> | {} | Custom props for rendering |

Behavior

  • Items in _meta.json appear in the order defined
  • Items not in _meta.json are appended at the end (sorted alphabetically)
  • Nested directories use their own _meta.json

Custom Frontmatter

interface MyFrontmatter {
  title: string;
  description: string;
  author: string;
  tags: string[];
}

const { frontmatter } = await parseMdx<MyFrontmatter>({
  contentDir: "contents/docs",
  slug: "article",
});

// frontmatter.title, frontmatter.author, etc. are typed!

Full Next.js Example

// lib/docs.ts - Create docs instance once
import { createDocs } from "@ariadocs/react";
import {
  remarkGfm,
  rehypePrism,
  rehypeSlug,
  rehypeAutolinkHeadings,
  rehypeCodeTitles,
} from "@ariadocs/react/plugins";

export const docs = createDocs({
  contentDir: "contents/docs",
  remarkPlugins: [remarkGfm],
  rehypePlugins: [
    rehypeSlug,
    rehypeAutolinkHeadings,
    rehypeCodeTitles,
    rehypePrism,
  ],
});
// app/docs/[[...path]]/page.tsx
import { docs } from "@/lib/docs";
import "@ariadocs/react/syntax.css";

export async function generateStaticParams() {
  const paths = await docs.getPagePaths();
  return paths.map((path) => ({ path: path.split("/").filter(Boolean) }));
}

export default async function DocPage({
  params,
}: {
  params: { path?: string[] };
}) {
  const slug = params.path?.join("/") ?? "index";

  const [{ MDX, frontmatter, toc }, navItems] = await Promise.all([
    docs.parse({ slug }),
    docs.getNavItems(),
  ]);

  return (
    <div className="flex">
      <aside>
        <NavList items={navItems} />
      </aside>
      <main>
        <h1>{frontmatter.title}</h1>
        <Toc items={toc} />
        {MDX}
      </main>
    </div>
  );
}

License

MIT © Nisab Mohd