{"record":{"id":"714b485d74ab81fa","repo":"withastro/astro","slug":"markdown-remarkplugins-markdown-rehypeplugins","errorCode":null,"errorMessage":"`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","messagePattern":"`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","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/config/validate.ts","lineNumber":82,"sourceCode":"\tif (!md) return;\n\n\tconst remarkPlugins = md.remarkPlugins ?? [];\n\tconst rehypePlugins = md.rehypePlugins ?? [];\n\tconst remarkRehype = md.remarkRehype ?? {};\n\tif (\n\t\tremarkPlugins.length === 0 &&\n\t\trehypePlugins.length === 0 &&\n\t\tObject.keys(remarkRehype).length === 0\n\t) {\n\t\treturn;\n\t}\n\n\tlet unified: typeof import('@astrojs/markdown-remark').unified;\n\tlet isUnifiedProcessor: typeof import('@astrojs/markdown-remark').isUnifiedProcessor;\n\ttry {\n\t\t({ unified, isUnifiedProcessor } = await import('@astrojs/markdown-remark'));\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'`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',\n\t\t);\n\t}\n\n\tconst current = md.processor;\n\tif (!current || isUnifiedProcessor(current)) {\n\t\t// `createRenderer` reads `.options.*` at render time, so in-place mutation propagates.\n\t\tconst target = (current ?? (md.processor = unified())) as ReturnType<typeof unified>;\n\t\tconst counts = migratedLegacyPluginCounts.get(target.options) ?? { remark: 0, rehype: 0 };\n\t\t// Only push entries past what we've already folded; mergeConfig appends, so they sit at the tail.\n\t\tif (remarkPlugins.length > counts.remark) {\n\t\t\ttarget.options.remarkPlugins.push(...remarkPlugins.slice(counts.remark));\n\t\t}\n\t\tif (rehypePlugins.length > counts.rehype) {\n\t\t\ttarget.options.rehypePlugins.push(...rehypePlugins.slice(counts.rehype));\n\t\t}\n\t\t// `remarkRehype` is an object, so Object.assign is idempotent for unchanged keys\n\t\t// and absorbs any new keys integrations add.","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/core/config/validate.ts#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Install the package: `npm install @astrojs/markdown-remark`.","If you don't need unified plugins, remove the three `markdown.*` config keys to stay on the default Sätteri processor.","After installing, ensure your remark/rehype plugins are compatible with the installed `@astrojs/markdown-remark` major version."],"exampleFix":"// before — astro.config.mjs (package missing)\nimport { defineConfig } from 'astro/config';\nexport default defineConfig({\n  markdown: { remarkPlugins: [remarkToc] },\n});\n\n// after\nnpm install @astrojs/markdown-remark","handlingStrategy":"validation","validationCode":"const has = (a) => Array.isArray(a) ? a.length > 0 : a && Object.keys(a).length > 0;\nif (has(md.remarkPlugins) || has(md.rehypePlugins) || Object.keys(md.remarkRehype ?? {}).length) {\n  require.resolve('@astrojs/markdown-remark'); // throws if missing\n}","typeGuard":"function needsMarkdownRemark(md) {\n  return Boolean((md.remarkPlugins?.length) || (md.rehypePlugins?.length) || (md.remarkRehype && Object.keys(md.remarkRehype).length));\n}","tryCatchPattern":null,"preventionTips":["When adopting remark/rehype plugins, install @astrojs/markdown-remark explicitly.","Pin the package version compatible with your Astro major.","Document the dependency in the project README."],"tags":["markdown","configuration","dependency-resolution","migration"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}