Render markdown to Solid components.
solid-markdown now tracks the react-markdown 10.x API closely and keeps the rendering pipeline upstream-aligned while adapting the JSX output to Solid.
This package is now a real Solid port of modern react-markdown, not a compatibility wrapper around older behavior. The public API matches upstream concepts such as components, remarkRehypeOptions, urlTransform, and async plugin support, while Solid gets one extra client-side helper: MarkdownResource.
bun add @rigelbuild/solid-markdownimport Markdown from "@rigelbuild/solid-markdown";
import remarkGfm from "remark-gfm";
const markdown = `
# This is a title
- here's
- a
- list
`;
export default function App() {
return <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>;
}| Export | Type | Purpose |
|---|---|---|
Markdown |
component | Synchronous markdown renderer. |
MarkdownAsync |
function | Async/server renderer for async unified plugins. |
MarkdownResource |
component | Solid client wrapper for async plugin pipelines. |
defaultUrlTransform |
function | Default URL sanitizer used for links and images. |
AllowElement |
type | Per-element allow/deny callback. |
Components |
type | Custom tag-to-component overrides. |
ExtraProps |
type | Extra props passed to custom components (node). |
Options |
type | Shared renderer options. |
MarkdownResourceOptions |
type | Options plus fallback. |
UrlTransform |
type | URL rewrite/sanitization hook. |
Synchronous markdown renderer.
import Markdown from "@rigelbuild/solid-markdown";
import remarkGfm from "remark-gfm";
<Markdown remarkPlugins={[remarkGfm]}>{value()}</Markdown>;Async/server helper for async unified plugins.
import { MarkdownAsync } from "@rigelbuild/solid-markdown";
import rehypeStarryNight from "rehype-starry-night";
const content = await MarkdownAsync({
children: "```js\nconsole.log(3.14)\n```",
rehypePlugins: [rehypeStarryNight],
});
return <div class="preview">{content}</div>;Solid-native client wrapper for async plugins.
import { MarkdownResource } from "@rigelbuild/solid-markdown";
import rehypeStarryNight from "rehype-starry-night";
<MarkdownResource
children={value()}
fallback={<p>Rendering…</p>}
rehypePlugins={[rehypeStarryNight]}
/>;By default, unsafe protocols such as javascript: are removed while standard URLs, fragments, and paths are preserved.
import Markdown, { defaultUrlTransform } from "@rigelbuild/solid-markdown";
<Markdown
urlTransform={(url, key, node) => {
const safe = defaultUrlTransform(url);
if (!safe) return safe;
return key === "href" && node.tagName === "a" ? `/out?url=${encodeURIComponent(safe)}` : safe;
}}
>
{"[OpenAI](https://openai.com)"}
</Markdown>;Supported options match upstream react-markdown 10.x semantics:
| Option | Purpose |
|---|---|
allowElement |
Decide per HAST element whether it should render. |
allowedElements |
Allowlist tag names. |
children |
Markdown source string. null and undefined render nothing. |
components |
Override specific HTML tags with Solid components or tag names. |
disallowedElements |
Blocklist tag names. |
rehypePlugins |
Rehype plugins applied after markdown is converted to HAST. |
remarkPlugins |
Remark plugins applied while parsing markdown. |
remarkRehypeOptions |
Extra remark-rehype options merged with the safe defaults used by upstream. |
skipHtml |
Ignore raw HTML in the markdown source. |
unwrapDisallowed |
Keep children of removed nodes instead of dropping the whole subtree. |
urlTransform |
Rewrite or sanitize link and image URLs. |
Custom components receive normal Solid intrinsic props plus node.
import Markdown, { type Components } from "@rigelbuild/solid-markdown";
const components: Components = {
code(props) {
return <code data-tag={props.node?.tagName}>{props.children}</code>;
},
};
<Markdown components={components}>{"`example`"}</Markdown>;Before:
import { SolidMarkdown } from "@rigelbuild/solid-markdown";
<SolidMarkdown children={markdown} />;After:
import Markdown from "@rigelbuild/solid-markdown";
<Markdown>{markdown}</Markdown>;Before:
<Markdown class="markdown-body">{markdown}</Markdown>;After:
<div class="markdown-body">
<Markdown>{markdown}</Markdown>
</div>Before:
<Markdown transformLinkUri={(href) => href} transformImageUri={(src) => src}>
{markdown}
</Markdown>;After:
<Markdown
urlTransform={(url, key) => {
if (key === "href") return url;
if (key === "src") return url;
return url;
}}
>
{markdown}
</Markdown>;SolidMarkdownis gone. Use the default export instead.- Wrapper props such as
classandclassNameare gone. WrapMarkdownin your own element. - Legacy pre-v9 props now throw at runtime instead of being silently accepted. This includes deprecated names such as
source,plugins,renderers,allowNode,allowedTypes,disallowedTypes,transformLinkUri,transformImageUri,linkTarget, and the old source-position props. urlTransformreplacestransformLinkUriandtransformImageUri.renderingStrategy="memo" | "reconcile"is a supported, Solid-specific prop on the synchronousMarkdownexport. This fork keeps it un-deprecated:"reconcile"is load-bearing for streaming DOM stability (it rebuilds the subtree each tick so growing content stays consistent), and consumers rely on it permanently.
Local verification for this port currently runs through:
bun run lint
bun run typecheck
bun run test
bun run buildCurrent status:
- Sync rendering is supported through
Markdown. - Async unified plugins are supported on the server through
MarkdownAsync. - Async unified plugins are supported on the client through
MarkdownResource. - SSR and DOM behavior are covered by the package test suite.