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

magic-docs

v1.0.5

Published

Reusable Fumadocs foundation: site contract, layout and MDX presets, TypeScript API docs, static export helpers, and visual theme.

Readme

How it works

  1. defineMagicDocs validates one site contract (name, URLs, package) at config evaluation time, and every helper below derives from it.
  2. Presets style Fumadocs with the shared theme. Reference tables and llms.txt output generate at build time.
  3. createMagicDocsStaticExport turns the app into a static Next export that GitHub Pages serves under a project path.
// lib/site.ts
import { createMagicDocsPublicPaths, defineMagicDocs } from "magic-docs";

export const site = defineMagicDocs({
  name: "React Native Magic Modal",
  description: "An imperative, promise-based modal library for React Native.",
  siteUrl: "https://gstj.github.io/react-native-magic-modal",
  repository: "https://github.com/GSTJ/react-native-magic-modal",
  packageName: "react-native-magic-modal",
});

export const publicPaths = createMagicDocsPublicPaths(site);

This is intentionally a preset. Content stays in each package repository; the preset centralizes the parts that should not drift. Packages still own their examples, guides, API decisions, and deploy workflow.

Install

Install the framework packages in the docs application:

pnpm add next@16 react react-dom fumadocs-core fumadocs-mdx \
  fumadocs-ui@npm:@fumadocs/base-ui magic-docs
pnpm add -D typescript tailwindcss @tailwindcss/postcss

The fumadocs-ui alias selects Base UI while preserving Fumadocs' documented import paths. Pin TypeScript separately from this monorepo's root: the docs app needs a compiler release Next and Fumadocs have already caught up to. Exact ranges for everything else live in this package's peerDependencies.

Site contract

The defineMagicDocs call above is the whole contract: define the package once and reuse the result everywhere. Invalid or relative URLs fail during config evaluation. siteUrl includes the GitHub Pages project path; that path is the source of truth for every public URL below.

Theme

For package consumption:

/* app/global.css */
@import "tailwindcss";
@import "magic-docs/theme.css";

To keep a repository-owned snapshot instead:

pnpm exec magic-docs-init --out app/magic-docs.css

The command is idempotent, refuses to overwrite a customized copy without --force, and also creates public/.nojekyll. Import the copied file after Tailwind:

@import "tailwindcss";
@import "./magic-docs.css";

A package can override the font variables without forking the theme:

:root {
  --font-magic-sans: var(--font-geist-sans);
  --font-magic-mono: var(--font-geist-mono);
}

Layout and MDX

Keep the classic docs layout: it has persistent navigation, search, and a table of contents.

// app/(docs)/layout.tsx
import { createMagicDocsLayout } from "magic-docs/fumadocs";
import { DocsLayout } from "fumadocs-ui/layouts/docs";

import { site } from "@/lib/site";
import { source } from "@/lib/source";

export default function Layout({ children }: LayoutProps<"/">) {
  return (
    <DocsLayout {...createMagicDocsLayout(site)} tree={source.getPageTree()}>
      {children}
    </DocsLayout>
  );
}

createMagicDocsLayout keeps those controls in sync across packages. Add package-specific links through its links option.

The shared MDX vocabulary includes Fumadocs' default cards, callouts, headings, and code blocks plus accordions, file trees, steps, tabs, and type tables:

// mdx-components.tsx
import { createMagicDocsMdxComponents } from "magic-docs/mdx";

export const getMDXComponents = createMagicDocsMdxComponents;
export const useMDXComponents = createMagicDocsMdxComponents;

Type tables

Use build-time generation. Paths stay relative to the MDX file and static export does not need the TypeScript filesystem at runtime.

// source.config.ts
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
import { magicDocsLlmMdxOptions } from "magic-docs/llms";
import { createMagicDocsTypeScript } from "magic-docs/typescript";

const typescript = createMagicDocsTypeScript({
  cacheDirectory: ".next/fumadocs-typescript",
});

export const docs = defineDocs({
  dir: "content/docs",
  docs: {
    postprocess: {
      includeProcessedMarkdown: magicDocsLlmMdxOptions,
    },
  },
});

export default defineConfig({
  mdxOptions: {
    remarkPlugins: [typescript.remarkPlugin],
  },
});

Then reference a public type:

<auto-type-table path="../../../src/types.ts" name="ModalProps" />

Generated tables supplement prose. fumadocs-typescript renders object and interface properties well, but:

  • top-level functions generate no useful entries;
  • enums can expose inherited String prototype members;
  • property @description tags do not become the table summary.

Document functions and enums manually, and use normal JSDoc prose on object properties. Keep generated reference pages behind task-first quickstarts and guides.

Agent-readable docs

magicDocsLlmMdxOptions is not optional when generated TypeTables are present. Without its TypeTable placeholder, Fumadocs serializes the generated prop's full JSON/ESTree into processed Markdown.

Use the shared renderer for page Markdown and llms-full.txt:

import { createMagicDocsLlmPage } from "magic-docs/llms";

import { site } from "@/lib/site";

export async function getLlmText(page: (typeof source)["$inferPage"]) {
  return createMagicDocsLlmPage(site, {
    title: page.data.title,
    description: page.data.description,
    url: page.url,
    processedMarkdown: await page.data.getText("processed"),
  });
}

It renders each prop as a Markdown row with its type and default.

Fumadocs' llms(source).index() emits application-relative links. Prefix them before returning llms.txt:

import { llms } from "fumadocs-core/source";
import { prefixMagicDocsLlmLinks } from "magic-docs/llms";

export function GET() {
  return new Response(prefixMagicDocsLlmLinks(site, llms(source).index()));
}

Static export on GitHub Pages

The portable Next config is:

// next.config.ts
import type { NextConfig } from "next";
import { createMDX } from "fumadocs-mdx/next";
import { createMagicDocsStaticExport } from "magic-docs";

import { site } from "./lib/site";

const withMdx = createMDX();
const config = createMagicDocsStaticExport(site) satisfies NextConfig;

export default withMdx(config);

It configures the static-export settings GitHub Pages needs. It deliberately does not set assetPrefix: Next handles /_next assets from basePath and does not recommend assetPrefix for sub-path hosting.

basePath does not rewrite fetch URLs or strings. Use the shared paths for the places outside the Next router:

import { oramaStaticClient } from "fumadocs-core/search/client/orama-static";

import { publicPaths } from "@/lib/site";

const search = oramaStaticClient({ from: publicPaths.searchApi });

publicPaths.markdown(page.url); // copy-Markdown button URL
publicPaths.llms; // /<project>/llms.txt
publicPaths.llmsFull; // /<project>/llms-full.txt
publicPaths.url(page.url); // canonical/OG absolute URL

Static search also needs a static route:

export const revalidate = false;
export const { staticGET: GET } = createFromSource(source);

The Pages workflow must preserve hidden files:

- uses: actions/upload-pages-artifact@v4
  with:
    path: out
    include-hidden-files: true

Without include-hidden-files, the generated public/.nojekyll disappears during upload and GitHub may process the export with Jekyll.

Adoption before the first npm release

Create a real package tarball:

# in GSTJ/magic
pnpm --filter magic-docs pack --pack-destination ./artifacts

For a full pre-release adoption, copy the tarball into the consumer:

vendor/magic-docs-<version>.tgz

and install that committed artifact:

pnpm add ./vendor/magic-docs-<version>.tgz
pnpm exec magic-docs-init --out app/magic-docs.css

The frozen lockfile then depends only on a file committed in the consumer. After the first publish, replace the file:vendor/...tgz spec with the npm version; none of the imports or config change.

For a theme-only bootstrap, run the CLI from the tarball, commit the generated CSS and .nojekyll, and do not retain magic-docs as a dependency until it is published.

pnpm dlx ./artifacts/magic-docs-<version>.tgz \
  --out app/magic-docs.css

Content standard

Every package starts with this order:

  1. Overview and a concrete result.
  2. Install.
  3. Five-minute quickstart.
  4. Task-oriented guides and recipes.
  5. API reference.
  6. Troubleshooting and migration notes.

Reference pages are generated from source. Each public feature needs at least one runnable example and a plain-language explanation of when to use it.