🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis →
Socket
Book a DemoInstallSign in
Socket

@acdh-oeaw/mdx-lib

Package Overview
Dependencies
Maintainers
2
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@acdh-oeaw/mdx-lib

utilities and plugins for [`mdx`](https://mdxjs.com).

latest
npmnpm
Version
0.1.4
Version published
Maintainers
2
Created
Source

mdx lib

utilities and plugins for mdx.

how to install

npm i @acdh-oeaw/mdx-lib

how to use

configure types for supported locales and jsx:

// ./types/mdx.d.ts

// import type * as runtime from "astro/jsx-runtime";
import type * as runtime from "react/jsx-runtime";

declare module "mdx/types" {
	namespace JSX {
		type Element = runtime.JSX.Element;
		type ElementClass = runtime.JSX.ElementClass;
		type IntrinsicElements = runtime.JSX.IntrinsicElements;
	}
}

declare module "@acdh-ch/mdx-lib" {
	export interface MdxConfig {
		locales: "de" | "en";
	}
}

provide component mappings (in next.js the file location has to be mdx-components.tsx at the project root):

// ./mdx-components.ts

import { Link } from "@/components/link";

const components = {
	a: Link,
	/** ... */
};

declare global {
	type MDXProvidedComponents = typeof components;
}

export function useMDXComponents(): MDXProvidedComponents {
	return components;
}

create locale aware mdx compilers:

// ./lib/mdx/compile-mdx.ts

import { createMdxProcessors, run } from "@acdh-ch/mdx-lib";
// import * as runtime from "astro/jsx-runtime";
import * as runtime from "react/jsx-runtime";

const createProcessor = createMdxProcessors((locale) => {
	return {
		/** When using `astro` instead of `react`. */
		// elementAttributeNameCase: "html",
		// jsxImportSource: "astro",

		remarkPlugins: [],
		remarkRehypeOptions: {},
		rehypePlugins: [],
	};
});

export async function compileMdx(content: string, baseUrl: URL, locale: string) {
	const processor = await createProcessor(locale);
	const compiled = await processor.process(content);
	return run(compiled, { ...runtime, baseUrl, useMDXComponents });
}

note that in astro projects, you need to use the astro jsx runtime (astro/jsx-runtime), and set elementAttributeNameCase to "html".

plugins

with-custom-heading-ids

rehype plugin which finds <HeadingId id="abc" /> mdx components, and applies the id attribute to a parent heading element.

example:

## This is a heading <HeadingId id="my-custom-heading" />

when not using any other plugins, this would be transformed to a regular <h2> element. when using the rehype-slug plugin, this would add an auto-generated id attribute: <h2 id="this-is-a-heading">. when using this plugin, you can add custom ids to the heading: <h2 id="my-custom-heading">. the <HeadingId> component will be removed from the generated output.

with-footnotes

remark plugin which finds inline <Footnote> mdx components, and transforms them to markdown footnotes, i.e. footnoteReference and footnoteDefinition mdast nodes (requires remark-gfm).

example:

This is an important sentence.<Footnote>Found it on the internet.</Footnote>

will be transformed to:

<p>
	This is an important sentence.<sup
		><a
			href="#user-content-fn-1"
			id="user-content-fnref-1"
			data-footnote-ref
			aria-describedby="footnote-label"
			>1</a
		></sup
	>.
</p>
<section data-footnotes class="footnotes">
	<h2 class="sr-only" id="footnote-label">Footnotes</h2>
	<ol>
		<li id="user-content-fn-1">
			<p>
				Found it on the internet.
				<a
					href="#user-content-fnref-1"
					data-footnote-backref=""
					aria-label="Back to reference 1"
					class="data-footnote-backref"
					>↩</a
				>
			</p>
		</li>
	</ol>
</section>

The h2 text content and aria-labels for generated elements can be controlled by setting remarkRehypeOptions.

with-iframe-titles

rehype plugin which applies the text content of a component to its title attribute. this is meant to be used with components which render an <iframe>. accepts a components option listing the names of components to handle.

example:

<Embed src="https://example.com/iframe">This is **the** title.</Embed>

will be transformed to:

<iframe src="https://example.com/iframe" title="This is the title." />

with-image-imports

rehype plugin which transforms the src attribute of <img> elements and configured mdx components into esm imports, because most javascript frameworks provide image optimisation features which integrate with a bundler and work via import statements.

example:

<img src="./first-image.png" />

<Figure src="/second-image.png">This is the image caption.</Figure>

will be transformed to:

import __image1__ from "./first-image.png";
import __image2__ from "/home/stefan/my-project/public/second-image.png";

<img src={__image1__} />

<Figure src={__image2__}>This is the image caption.</Figure>

note that paths starting with "/" will be expanded to absolute paths using the publicPath config option (defaults to "/public/"). for relative paths starting with "./" or "../", you need to provide a baseUrl to the mdx compiler.

also note that you need a bundler to handle the image imports, so this requires outputFormat: "program".

don't forget to map img elements to a custom component which can handle objects as src prop, e.g.:

// ./mdx-components.ts

import Image from "next/image";

const components = {
	img: Image,
};

export function useMDXComponents() {
	return components;
}

with-table-of-contents

rehype plugin which generates a table of contents, and provides it via vfile.data.tableOfContents, as well as a tableOfContents named export. additionally, it provides the table of contents data to any TableOfContents mdx component.

const vfile = await processor.process(content);
console.log(vfile.data.tableOfContents);
const { default: Content, tableOfContents } = await run(vfile, { ...runtime });

example using all provided plugins

// ./lib/mdx/compile-mdx.ts

import {
	createMdxCompiler,
	createMdxProcessors,
	typographyConfig,
	withCustomHeadingIds,
	withFootnotes,
	withIframeTitles,
	// withImageImports,
	withImageSizes,
	withTableOfContents,
} from "@acdh-ch/mdx-lib";
import withHeadingIds from "rehype-slug";
import withGfm from "remark-gfm";
import withTypographicQuotes from "remark-smartypants";
import * as runtime from "react/jsx-runtime";

import { useMDXComponents } from "../../mdx-components";

const createProcessor = createMdxProcessors((locale) => {
	return {
		remarkPlugins: [withGfm, [withTypographicQuotes, typographyConfig[locale]], withFootnotes],
		remarkRehypeOptions: {
			footnoteBackLabel(referenceIndex, rereferenceIndex) {
				return t("footnoteBackLabel", {
					reference: referenceIndex + 1 + (rereferenceIndex > 1 ? "-" + rereferenceIndex : ""),
				});
			},
			footnoteLabel: t("footnoteLabel"),
		},
		rehypePlugins: [
			withCustomHeadingIds,
			withHeadingIds,
			[withIframeTitles, { components: ["Embed", "Iframe", "Video"] }],
			// [withImageImports, { components: ["Figure"] }],
			[withImageSizes, { components: ["Figure"] }],
			withTableOfContents,
		],
	};
});

export const compileMdx = createMdxCompiler(createProcessor, runtime, useMDXComponents);

FAQs

Package last updated on 28 Aug 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts