react-markdown-remote
v2.0.0
Published
Fetch a markdown file from a URL and render it in React, with relative images and links resolved against the source document.
Maintainers
Readme
react-markdown-remote
Fetch a markdown file from a URL and render it in React. Relative images and links resolve against that file, not against the page you dropped the component on.
This is the successor to react-markdown-to-html (2015). That package dumped marked HTML through dangerouslySetInnerHTML. This one does not. Parsing is react-markdown. The thing react-markdown does not do — load a document and rewrite its assets — is the whole API.
import { RemoteMarkdown } from "react-markdown-remote";
import "react-markdown-remote/styles.css"; // optional typography
<RemoteMarkdown src="https://github.com/acme/app/blob/main/docs/guide.md" />npm install react-markdown-remotePeer dependency: react >= 18.
Why this exists
react-markdown takes a string. You still have to fetch it, and then this happens:

[API](./api.md)Those paths are relative to the markdown file. On GitHub they work. In your app they 404, because the browser resolves them against the current page. GitHub blob URLs also cannot be used as img src — you need raw.githubusercontent.com for images and github.com/.../blob/... for links.
| You have | Use |
| --- | --- |
| A markdown string already in memory | react-markdown |
| A file at a URL, with ./ images and links | react-markdown-remote |
| JSX inside markdown | MDX |
What src accepts
| src | Fetches from | ./shot.png becomes |
| --- | --- | --- |
| https://github.com/org/repo/blob/main/docs/guide.md | raw.githubusercontent.com/.../docs/guide.md | raw image URL under docs/ |
| https://github.com/org/repo | README.md on HEAD | raw image at repo root |
| https://github.com/org/repo/tree/main/docs | docs/README.md | raw image under docs/ |
| https://gitlab.com/group/app/-/blob/main/README.md | GitLab /-/raw/ | GitLab raw image |
| https://gist.github.com/user/id | gist.githubusercontent.com/.../raw | gist raw |
| https://example.com/docs/guide.md | that URL | https://example.com/docs/shot.png |
| /docs/README.md | same origin (browser) | same-origin relative to the file |
On GitHub and GitLab, a path that starts with / is the repo root, matching how those hosts render README files. On a generic URL it is the host root.
Branches with slashes: use the refs/heads/ form, e.g. .../blob/refs/heads/feature/foo/README.md.
Relative rewrite
import { resolveDocument, rewriteUrl } from "react-markdown-remote";
const doc = resolveDocument("https://github.com/acme/app/blob/main/docs/guide.md");
rewriteUrl("./shot.png", doc, "image");
// https://raw.githubusercontent.com/acme/app/main/docs/shot.png
rewriteUrl("./extra.md", doc, "link");
// https://github.com/acme/app/blob/main/docs/extra.mdjavascript:, data:, vbscript:, and file: URLs are dropped. Absolute http(s) links are left alone. GitHub blob URLs used as images are converted to raw.
The component is a thin fetch + this rewrite + react-markdown. You can call loadMarkdown(src) on the server and render with your own pipeline.
API
<RemoteMarkdown
src="https://github.com/org/repo/blob/main/README.md"
loading={<p>Loading…</p>}
error={(err) => <p>{err.message}</p>}
onLoad={({ markdown, document }) => console.log(document.fetchUrl)}
components={{ a: (props) => <a {...props} target="_blank" /> }}
/>| Prop | Type | Default | Notes |
| --- | --- | --- | --- |
| src | string | required | Document URL |
| fetch | typeof fetch | globalThis.fetch | Use a proxy for hosts without CORS. Keep src as the real document URL |
| requestInit | RequestInit | — | Merged into the request (signal is owned by the component) |
| gfm | boolean | true | remark-gfm (tables, task lists, strikethrough) |
| loading | ReactNode | status text | null hides it |
| error | ReactNode | (Error) => ReactNode | alert with message | null hides it |
| onLoad | (result) => void | — | Markdown string + resolved document |
| onError | (Error) => void | — | Not called for aborted fetches |
| origin | string | window.location.href | Base for relative src |
All react-markdown options except children are forwarded (components, remarkPlugins, rehypePlugins, urlTransform, …). Your urlTransform runs after the relative rewrite, then defaultUrlTransform sanitizes.
Hooks and headless helpers:
import {
useRemoteMarkdown,
loadMarkdown,
resolveDocument,
rewriteUrl,
} from "react-markdown-remote";"use client" is prepended to the build for Next.js App Router.
CORS
raw.githubusercontent.com sends Access-Control-Allow-Origin: *. Many other hosts do not. If the browser blocks the request, pass fetch that hits your own proxy, and leave src pointing at the real file so rewrite still has the right base.
Do not enable rehype-raw unless you sanitize. Untrusted markdown is safe by default because this package never uses dangerouslySetInnerHTML and does not parse raw HTML.
Demo
npm install
npm run demoOpens http://localhost:5173 with a local fixture (relative image + link) and a GitHub README preset.
Migration from react-markdown-to-html
- import Markdown2HTML from "react-markdown-to-html"
- <Markdown2HTML src="README.md" />
+ import { RemoteMarkdown } from "react-markdown-remote"
+ <RemoteMarkdown src="https://github.com/org/repo/blob/main/README.md" />- React 18+ is required (
createClassis gone) - jQuery, lodash, and
markedare gone - Output is React elements, not HTML strings
- A bare
README.mdonly works if that file is served from the current origin
The old package name react-markdown-to-html is abandoned. Prefer npm deprecate on it.
License
ISC
