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

satteri-nextjs

v0.0.2

Published

Next.js integration for satteri — Rust-native Markdown/MDX, compiled to React/RSC modules.

Readme

satteri-nextjs

A Next.js integration for satteri — Rust-native Markdown/MDX parsing and compilation, emitted as React/RSC-compatible modules.

It's a @next/mdx-style plugin: drop it into next.config, and .md/.mdx files compile with satteri's mdxToJs (parse + compile in Rust) instead of the @mdx-js/* JavaScript toolchain — while keeping the conventions you expect (mdx-components.tsx, frontmatter, a table of contents, GFM).

  • Fast — parsing and MDX→JS compilation run in native Rust.
  • RSC-friendly — output needs no 'use client'; static subtrees collapse to HTML, interactive components stay JSX.
  • Both bundlers — works under webpack and Turbopack.
  • Tested against Next.js 14, 15, and 16 on both bundlers.

Installation

npm install satteri-nextjs satteri
# or: pnpm add satteri-nextjs satteri

satteri is a peer dependency (it ships the native binary). Requires Next.js ≥ 14 and React ≥ 18.

Quick start

1. Wrap your Next config:

// next.config.mjs
import withSatteri from 'satteri-nextjs';

export default withSatteri()({
  // your normal Next.js config
});

withSatteri automatically adds md and mdx to pageExtensions and wires the loader into both the webpack and Turbopack pipelines.

2. Write a page in MDX:

---
title: Hello
description: My first satteri page
---

# Hello world

Some **markdown** with a [link](https://example.com) and GFM ~~strike~~.

<Counter initial={3} />

Save it as app/hello/page.mdx and visit /hello. That's it.

Component provider (mdx-components.tsx)

To make components available to every MDX file without per-page imports, create an mdx-components.tsx in your project root (or src/) — the same convention as @next/mdx:

// mdx-components.tsx
import { Counter } from './components/Counter';

export function useMDXComponents(components) {
  return { Counter, ...components };
}

The file is optional — without it, a no-op provider is used and plain MDX still renders.

Note: static markdown is collapsed to an HTML string for performance (optimizeStatic), so provider overrides of base tags (h1, p, …) don't apply to that collapsed content — only real components (<Counter />) stay JSX and are resolved from the provider. Pass optimizeStatic: false if you need to override base tags.

Frontmatter & table of contents

Every compiled .md/.mdx module exports frontmatter (parsed YAML) and toc (collected headings, each slugged with an id) alongside the default component:

// app/blog/page.tsx
import Post, { frontmatter, toc } from './post.mdx';

export const metadata = { title: frontmatter.title };

export default function Page() {
  return (
    <>
      <h1>{frontmatter.title}</h1>
      <nav>
        <ul>
          {toc.map((h) => (
            <li key={h.id} style={{ marginLeft: (h.depth - 1) * 12 }}>
              <a href={`#${h.id}`}>{h.value}</a>
            </li>
          ))}
        </ul>
      </nav>
      <Post />
    </>
  );
}

To type these named exports on *.mdx imports, add an ambient declaration:

// mdx.d.ts
declare module '*.mdx' {
  import type { ComponentType } from 'react';
  const MDXContent: ComponentType<{ components?: Record<string, unknown> }>;
  export const frontmatter: Record<string, unknown>;
  export const toc: { depth: number; value: string; id: string }[];
  export default MDXContent;
}

Both exports are on by default. Disable them with withSatteri({ toc: false, frontmatter: false }). YAML frontmatter is parsed; TOML is not (it exports {} and warns).

Configuration

withSatteri(options) accepts:

| Option | Type | Default | Description | | ---------------------- | ----------------------------------------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | features | { gfm?, frontmatter?, math?, directive?, smartPunctuation?, … } | satteri defaults (GFM on) | satteri parser feature toggles. | | mdastPlugins | (plugin \| PluginSpec)[] | [] | satteri mdast plugins (~ remark). | | hastPlugins | (plugin \| PluginSpec)[] | [] | satteri hast plugins (~ rehype). | | optimizeStatic | OptimizeStaticConfig \| false | React-style | Collapse static subtrees to one HTML string. false emits per-node JSX. | | toc | boolean | true | Slug headings and export toc. | | frontmatter | boolean | true | Parse YAML frontmatter and export frontmatter. | | providerImportSource | string | managed | Where useMDXComponents is imported from. Override to use your own provider (e.g. @mdx-js/react); then you wire its alias yourself. | | jsxImportSource | string | "react" | JSX runtime import source. | | development | boolean | from build mode | Emit jsxDEV / source info. | | extension | RegExp | /\.mdx?$/ | Which files to treat as Markdown/MDX. |

Plugins

satteri uses its own plugin model (mdastPlugins / hastPlugins), not remark/rehype — a generic remark/rehype shim isn't viable because satteri's AST nodes are read-only views over a Rust arena. Write satteri-native plugins (via defineMdastPlugin / defineHastPlugin from satteri), or use the ready-made ones shipped here.

Built-in: externalLinks

Adds target/rel to off-site anchors (~ rehype-external-links):

import withSatteri from 'satteri-nextjs';
import { externalLinks } from 'satteri-nextjs/plugins';

export default withSatteri({
  hastPlugins: [externalLinks({ target: '_blank', rel: 'noopener noreferrer' })],
})({});

Plugins under Turbopack — string specs

Turbopack loader options must be JSON-serializable, so imported plugin functions/objects apply under webpack only (Turbopack compiles without them and a warning is printed). To use a plugin under both bundlers, reference it by a serializable string spec instead:

export default withSatteri({
  // 'module#exportName', or ['module#exportName', options]
  hastPlugins: [
    'satteri-nextjs/plugins#externalLinks',
    ['satteri-nextjs/plugins#externalLinks', { target: '_top' }],
  ],
})({});

The export is called as factory(options) (or used as-is if it isn't a function) at compile time via dynamic import.

Using the compiler directly

To compile a string yourself (e.g. user-generated content, a non-route module), use compileMdx:

import { compileMdx } from 'satteri-nextjs/loader';

const { code, frontmatter, data } = await compileMdx(source, {
  hastPlugins: [
    /* … */
  ],
});
// `code` is an ESM module string; `data.toc` holds collected headings.

Exports

| Specifier | Exports | | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | satteri-nextjs | withSatteri (default), compileMdx, collectHeadings, parseFrontmatter, externalLinks, isPluginSpec, types | | satteri-nextjs/loader | the loader (default) + compileMdx, resolveDevelopment | | satteri-nextjs/plugins | externalLinks (and future built-in plugins) | | satteri-nextjs/mdx-components-fallback | the no-op provider used when no mdx-components file exists |

Compatibility notes

  • Next.js version: the Turbopack config key moved from experimental.turbo (13.0–15.2) to top-level turbopack (15.3+); withSatteri detects the installed version and writes to the correct key automatically.
  • Custom provider: if you pass your own providerImportSource, withSatteri stops managing the mdx-components alias — wire it up yourself.

License

MIT