withastro/astro · error · Error

`markdown.remarkPlugins`, `markdown.rehypePlugins`, and `mar

Error message

`markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` run on the `unified` processor from `@astrojs/markdown-remark`, which is no longer installed by default now that Sätteri is the default Markdown processor. Install it with:
  npm install @astrojs/markdown-remark

What it means

When Sätteri is the default Markdown processor (so `@astrojs/markdown-remark` is not bundled), Astro lazily imports that package only if the user configured `markdown.remarkPlugins`, `markdown.rehypePlugins`, or `markdown.remarkRehype`. If the dynamic `import('@astrojs/markdown-remark')` rejects, Astro throws a plain `Error` instructing the user to install it. This keeps the default install lean while still supporting the unified-based plugin pipeline on demand.

Source

Thrown at packages/astro/src/core/config/validate.ts:82

	if (!md) return;

	const remarkPlugins = md.remarkPlugins ?? [];
	const rehypePlugins = md.rehypePlugins ?? [];
	const remarkRehype = md.remarkRehype ?? {};
	if (
		remarkPlugins.length === 0 &&
		rehypePlugins.length === 0 &&
		Object.keys(remarkRehype).length === 0
	) {
		return;
	}

	let unified: typeof import('@astrojs/markdown-remark').unified;
	let isUnifiedProcessor: typeof import('@astrojs/markdown-remark').isUnifiedProcessor;
	try {
		({ unified, isUnifiedProcessor } = await import('@astrojs/markdown-remark'));
	} catch {
		throw new Error(
			'`markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` run on the `unified` processor from `@astrojs/markdown-remark`, which is no longer installed by default now that Sätteri is the default Markdown processor. Install it with:\n  npm install @astrojs/markdown-remark',
		);
	}

	const current = md.processor;
	if (!current || isUnifiedProcessor(current)) {
		// `createRenderer` reads `.options.*` at render time, so in-place mutation propagates.
		const target = (current ?? (md.processor = unified())) as ReturnType<typeof unified>;
		const counts = migratedLegacyPluginCounts.get(target.options) ?? { remark: 0, rehype: 0 };
		// Only push entries past what we've already folded; mergeConfig appends, so they sit at the tail.
		if (remarkPlugins.length > counts.remark) {
			target.options.remarkPlugins.push(...remarkPlugins.slice(counts.remark));
		}
		if (rehypePlugins.length > counts.rehype) {
			target.options.rehypePlugins.push(...rehypePlugins.slice(counts.rehype));
		}
		// `remarkRehype` is an object, so Object.assign is idempotent for unchanged keys
		// and absorbs any new keys integrations add.

View on GitHub (pinned to d081033d5f)

Solutions

  1. Install the package: `npm install @astrojs/markdown-remark`.
  2. If you don't need unified plugins, remove the three `markdown.*` config keys to stay on the default Sätteri processor.
  3. After installing, ensure your remark/rehype plugins are compatible with the installed `@astrojs/markdown-remark` major version.

Example fix

// before — astro.config.mjs (package missing)
import { defineConfig } from 'astro/config';
export default defineConfig({
  markdown: { remarkPlugins: [remarkToc] },
});

// after
npm install @astrojs/markdown-remark
Defensive patterns

Strategy: validation

Validate before calling

const has = (a) => Array.isArray(a) ? a.length > 0 : a && Object.keys(a).length > 0;
if (has(md.remarkPlugins) || has(md.rehypePlugins) || Object.keys(md.remarkRehype ?? {}).length) {
  require.resolve('@astrojs/markdown-remark'); // throws if missing
}

Type guard

function needsMarkdownRemark(md) {
  return Boolean((md.remarkPlugins?.length) || (md.rehypePlugins?.length) || (md.remarkRehype && Object.keys(md.remarkRehype).length));
}

Prevention

When it happens

Trigger: Configuring any of `markdown.remarkPlugins`, `markdown.rehypePlugins`, or `markdown.remarkRehype` in `astro.config` without having `@astrojs/markdown-remark` installed. The `validate.ts` check triggers the dynamic import, which fails.

Common situations: Migrating from a previous Astro version where `@astrojs/markdown-remark` was a default dependency; copying remark/rehype plugin config from a tutorial into a fresh project on the new default processor; a pnpm setup where the package isn't hoisted.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/714b485d74ab81fa. Report an issue: GitHub.