MDX evaluate with react and renderToStaticMarkup: working versions (tested 2026-10-17)
Compile and evaluate an MDX string offline with @mdx-js/mdx, inject a component through the components prop, renderToStaticMarkup the default export and assert both the HTML and an exported const.
passed1 passed · 0 failed2026-10-17 19:19 UTC- Status
- passed
- Last passed
- 2026-10-17 19:19 UTC
- Tests
- 1 / 1
- Language
- TypeScript
- Runtime
- node v22.23.3
npm install --no-audit --no-fund --ignore-scripts=false npm test
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 39061946720
The code
Exactly the files that passed. Your agent can pull them with get_recipe("mdx-compile-evaluate") (install), or see them on GitHub.
{
"name": "mdx-compile-evaluate",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"@mdx-js/mdx": "3.1.1",
"react": "19.3.1",
"react-dom": "19.3.1"
},
"devDependencies": {
"@types/react": "19.3.0",
"@types/react-dom": "19.3.0",
"@types/node": "26.6.5",
"tsx": "4.23.15"
}
}
import { evaluate } from "@mdx-js/mdx";
import * as runtime from "react/jsx-runtime";
import type { ComponentType } from "react";
export type MdxComponent = ComponentType<Record<string, unknown>>;
export type MdxComponentMap = Record<string, MdxComponent>;
export type MdxResult = {
Content: ComponentType<{ components?: MdxComponentMap }>;
exports: Record<string, unknown>;
};
export async function evaluateMdx(source: string): Promise<MdxResult> {
const mod = await evaluate(source, {
...runtime,
baseUrl: import.meta.url,
});
const { default: Content, ...exports } = mod;
return {
Content: Content as unknown as MdxResult["Content"],
exports: exports as Record<string, unknown>,
};
}
import { test } from "node:test";
import assert from "node:assert/strict";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { evaluateMdx, type MdxComponent } from "../src/render.ts";
function Greeting({ name }: { name: string }) {
return createElement("p", { className: "greeting" }, `Hello, ${name}!`);
}
const mdx = [
"# Hello MDX",
"",
"export const answer = 42;",
"",
'<Greeting name="World" />',
"",
"The answer is {answer}.",
].join("\n");
test("evaluates frontmatter-free MDX and renders static HTML", async () => {
const { Content, exports } = await evaluateMdx(mdx);
const html = renderToStaticMarkup(
createElement(Content, { components: { Greeting: Greeting as MdxComponent } }),
);
assert.match(html, /<h1>Hello MDX<\/h1>/);
assert.match(html, /<p class="greeting">Hello, World!<\/p>/);
assert.match(html, /<p>The answer is 42\.<\/p>/);
assert.equal(exports.answer, 42);
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
i18next + react-i18next: plurals, interpolation and SSR rendering
Configure i18next with resources, plurals, context and escaping, and render react-i18next components to HTML on the server, with per-request instances.
Ink interactive CLI components tested with ink-testing-library
Build an interactive Ink 8 (React) terminal checklist with useInput and a timer-driven countdown, and test frames, arrow-key and space input, submit callbacks and effects with ink-testing-library.
Jotai atoms with React 19, tested with Testing Library and isolated stores
Jotai 3 primitive, derived, write-only and async atoms driving a React 19 todo UI with Suspense, tested in Vitest on happy-dom with @testing-library/react, user-event and a fresh createStore per test, plus store-only tests without React.
Next.js 16 App Router route handler + Zod validation
Validate JSON bodies and search params in a Next.js App Router route handler with Zod, returning typed 400s; tested by calling the handler with NextRequest.
All @mdx-js/mdx recipesAll react recipesAll react-dom recipes