---
url: /guide/introduction.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# Overview
`react-wikitext` renders MediaWiki-style wikitext as React elements. Source text is parsed into a typed semantic AST, converted to HAST, and handed to React. It does not talk to the MediaWiki API, execute Lua, or own your routes.
## What it solves
A common shortcut is to inject pre-rendered HTML with `dangerouslySetInnerHTML`, or to embed a wiki page in an `iframe`. Neither composes well: the first bypasses React, and the second cannot reuse your styles or interactions.
`react-wikitext` outputs React elements instead of HTML strings. It exposes an inspectable semantic tree and a sanitized HAST before rendering, leaves links, images, and templates to the host, and can parse and prepare on the server, in a worker, or behind a cache while the client receives a snapshot.
## Capabilities
| Capability | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Parse and render | `wikiparser-node@1.48.1` produces a syntax tree; the library normalizes it into a typed AST and renders React |
| Snapshots and server | Parse and render snapshots are plain data; prepare on the server and ship only `/view` |
| Template expansion | Optional `/expand` with a host resolver for supported templates and pure wikitext functions |
| Customization and debugging | Component overrides, `handlers`, rehype plugins, CSS variables, labels, and diagnostics |
| unified | `/unified` exposes the pipeline as raw-mode plugins for a processor you own |
### Default output
The example below configures no resolvers. Template and file placeholders and non-navigable links are expected defaults, not render failures:
```tsx preview
import { Wikitext } from "react-wikitext";
import "react-wikitext/styles.css";
const source = `== Default output ==
This paragraph mixes '''bold''', ''italic'', and an [[internal link|internal link]].
{{Infobox|title=Templates are not evaluated by default}}
[[File:Example.png|thumb|Files are placeholders by default]]`;
export default function Demo() {
return ;
}
```
See [Rendering behavior](/guide/rendering.md) and [Syntax support](/guide/syntax-support.md) for the full boundaries.
## Deliberate limits
The default pipeline does not evaluate templates or parser functions, and it never runs Lua; `/expand` covers a supported subset only. The library makes no network requests, does not load images by default, and leaves internal links non-navigable.
It is not a MediaWiki implementation and includes no editor or virtual list. The site API, editing, full extension semantics, and pagination belong to the host. See [Rendering behavior](/guide/rendering.md) for details.
## When it fits
It suits read-only wiki mirrors, archives, and exports, and wiki snippets embedded in knowledge bases, documentation sites, or admin consoles. It also fits cases that need structured content before rendering, per-block fault isolation, parsing in a worker, or links, images, and templates wired to your own router, CDN, and backend.
Other tools fit better in these cases:
| Case | Better fit |
| ------------------------------------------- | -------------------------------------------------------- |
| The content is already Markdown | A Markdown renderer such as `react-markdown` |
| You need full site-level wiki semantics | Parse output from MediaWiki / Parsoid |
| You only need to isolate a third-party page | An `iframe`, at the cost of host styling and interaction |
| You need WYSIWYG editing | An editor; this library only renders read-only content |
## Current status
:::note
Version `0.1.0-alpha.1`, published on the alpha dist-tag and installed with `pnpm add react-wikitext@alpha`. Releases in the 0.x range follow semantic versioning: minor versions may contain breaking changes, and patch versions only carry compatible fixes. React `>=18.2 <20`; Node `>=18.18` for builds or server execution. The parser is pinned to `wikiparser-node@1.48.1`; the license is GPL-3.0-or-later.
:::
See [Status and compatibility](/project/status.md) for the full matrix.
## Next steps
- [Quick start](/guide/quick-start.md): install and render a first snippet.
- [How it works](/guide/concepts.md): the pipeline, snapshots, and entry boundaries.
- [Syntax support](/guide/syntax-support.md): whether a construct renders, becomes a placeholder, or degrades.
---
url: /guide/quick-start.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# Quick Start
This page renders a snippet. For pipeline, snapshot, and entry trade-offs, see [How it works](/guide/concepts.md).
## Requirements
- React `>=18.2 <20` (peer dependency).
- Node `>=18.18` for builds or server execution.
- ESM and CJS builds; consumer builds with Vite and webpack 5 are covered by tests.
- No Wikimedia, font CDN, or external API dependency at runtime.
## Installation
```bash
pnpm add react-wikitext@alpha
```
Releases currently ship on the alpha dist-tag, so the install command names the channel explicitly; once a stable version is out, `pnpm add react-wikitext` is enough. The stylesheet is imported once:
```ts
import "react-wikitext/styles.css";
```
::::note
The npm package only contains `dist/`, `NOTICE`, and `THIRD_PARTY_LICENSES`. See the [contributing guide](/project/contributing.md) for building from source and local debugging.
::::
## Minimal example
The `Wikitext` component from the root entry handles parsing, preparation, and rendering. Import the stylesheet once:
```tsx preview
import { Wikitext } from "react-wikitext";
import "react-wikitext/styles.css";
const source = `== Hello, wikitext ==
This paragraph mixes '''bold''', ''italic'', and an [[internal link|internal link]].
* First item
* Second item`;
export default function Article() {
return ;
}
```
`Wikitext` renders a root `div.wtr-view`. `lang` and `dir` are written to that container for layout and assistive technology.
::::warning
`Wikitext` and `WikitextView` depend on React Context and only run in Client Components. The root entry and `/view` already ship a `"use client"` directive, so they can be used directly in Next.js App Router; the server should only call React-free entries such as `/parse` and `/hast`, as shown in [server preparation](/guide/server-rendering.md).
::::
## What you get
By default:
- Headings, paragraphs, bold, italic, lists, definition lists, tables, code blocks, and references render as structured output.
- Internal links are **non-navigable text**; provide `resolveLink` when you have routes.
- External links use the safe source URL and carry `target="_blank"` and `rel="nofollow noopener noreferrer"`.
- Templates, parser functions, and files render explicit placeholders; their content is not inferred.
- Files render as placeholders; only an address returned by `resolveImage` and validated by the library produces ``.
- A table of contents is generated automatically once a page has four headings; categories are collected into a footer.
- Unknown structures degrade to source cards, so the page continues to render.
See [Syntax support](/guide/syntax-support.md) for per-construct status and [Rendering behavior](/guide/rendering.md) for details.
## Next steps
- [How it works](/guide/concepts.md): why parsing and preparation are separate, and when to use `/view`.
- [Basic usage](/guide/usage.md): explicit pipeline, async plugins, and component overrides.
- [Links and images](/guide/links-and-images.md): connect your router and media service.
- [Server rendering](/guide/server-rendering.md): server preparation, serialization, and hydration.
- [Troubleshooting](/guide/troubleshooting.md): placeholders, errors, and configuration issues.
- [Entry points](/api/index.md): what each subpath exports.
---
url: /guide/concepts.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# How It Works
The library splits rendering into stages that can be deployed separately. Understanding the pipeline and the two snapshots makes it obvious when to use the root entry and when to parse on the server.
## Pipeline
```text
source
│
├─ (optional) /expand ─▶ expanded source
│
▼
/parse ─▶ WikitextDocument (parse snapshot)
│
▼
/hast prepare ─▶ WikitextRenderDocument (render snapshot)
│
▼
/view ─▶ React
```
| Stage | Entry | Input | Output | Required |
| ----------- | ------------------- | ------------------------------ | ------------------------ | ---------------- |
| Expansion | `/expand` | Source and a template resolver | Expanded parse document | Optional |
| Parsing | `/parse`, `/ast` | Source | `WikitextDocument` | Yes |
| Preparation | `/hast` | Parse snapshot | `WikitextRenderDocument` | Before rendering |
| Rendering | `/view`, root entry | Render snapshot | React elements | To render |
The result of `/expand` is already a parse document. Do not parse it again; pass it to `prepareWikitext`.
## Two snapshots
### Parse snapshot `WikitextDocument`
```ts
interface WikitextDocument {
readonly source: string;
readonly ast: WikitextRoot;
readonly syntax?: WikitextSyntaxRoot;
readonly meta: WikitextDocumentMeta;
readonly diagnostics: readonly WikitextDiagnostic[];
}
```
- `ast` is the normalized semantic tree; preparation never mutates it.
- `syntax` exists only with `retainSyntax: true`, for source positions and the playground.
- The same source with a different site config or `pageName` can produce a different AST.
### Render snapshot `WikitextRenderDocument`
```ts
interface WikitextRenderDocument {
readonly source: string;
readonly ast: WikitextRoot; // working tree after plugins
readonly hast: import("hast").Root; // final sanitized HAST
readonly headings: readonly WikitextHeadingInfo[];
readonly meta: WikitextRenderMeta;
readonly diagnostics: readonly WikitextDiagnostic[];
}
```
- `hast` already contains the TOC, reference section, and category footer.
- `ast` is the working tree after plugins; node ids have been reassigned.
- `meta` records the effective policy, labels, heading strategy, and authorized images.
- `WikitextView` accepts only a render snapshot, never source text.
Queries must use the right snapshot: do not look up ids from a render snapshot in the original parse snapshot.
## Why two stages
Parsing is expensive, while preparation options change often. Cache the parse snapshot and prepare it again with different resolvers, images, labels, or plugins instead of reparsing. Server preparation and client rendering use the same snapshot: the client loads no parser and reruns no plugins.
Preparation works on a cloned tree, so the parse snapshot stays read-only. Snapshots are plain data: they can be cached, sent through a worker, or transferred over the network, and restore functions revalidate structure and budgets.
:::note
Preparation is not an optional "touch-up" step. It determines the final HAST content and the security boundary; handing unprocessed HAST to the view is unsupported.
:::
## Semantic normalization
The upstream parser returns a syntax tree. The library normalizes structure during the semantic stage; the React layer never scans source text.
Structures that cannot be represented safely or correctly degrade to a source card and a diagnostic; the library does not guess. Templates, parser functions, arguments, and files render explicit placeholders, so "not evaluated" is never mistaken for "evaluated".
Source HTML is handled by category: allowlisted tags render, forbidden tags are dropped with a readable hint, and unknown tags degrade. Links and images are validated again during finalization: protocol, `target`, `rel`, and image authorization.
The goal is explainable, auditable output, not maximal Wikipedia fidelity.
## Entry boundaries
| Entry | Loads parser | Loads React | Typical use |
| ---------- | ------------ | ----------- | -------------------------------- |
| Root entry | Yes | Yes | Render source on the client |
| `/view` | No | Yes | Render a prepared snapshot |
| `/parse` | Yes | No | Server, worker, cache |
| `/expand` | Yes | No | Expand templates before parse |
| `/hast` | No | No | Prepare, serialize, restore |
| `/ast` | No | No | AST queries and data pipelines |
| `/unified` | No | No | Raw mode with an injected parser |
| `/configs` | No | No | Site config constants |
See [Entry points](/api/index.md) for the full export list.
:::warning
React entries create Context at module load and use `useId` / `useMemo`, so they only run in Client Components. The root entry and `/view` ship a `"use client"` directive, so they work directly in Next.js App Router and similar environments. On the server, only call React-free entries such as `/parse` and `/hast`.
:::
## Glossary
| Term | Meaning |
| --------------- | ---------------------------------------------------------------------------------------------------- |
| wikitext | MediaWiki-style page source |
| Parse snapshot | The semantic result of source plus site config (`WikitextDocument`) |
| Render snapshot | The result after plugins, HAST conversion, sanitization, and finalization (`WikitextRenderDocument`) |
| Resolver | Host callbacks such as `resolveLink`, `resolveImage`, `resolveTemplate` |
| Handler | A `prepareOptions.handlers` function converting a custom semantic node to HAST |
| Policy | URL, tag, and class rules in `prepareOptions.policy` |
| Labels | `prepareOptions.labels`, every user-visible placeholder and hint |
## Next steps
- [Basic usage](/guide/usage.md): source component and explicit pipeline.
- [Server rendering](/guide/server-rendering.md): snapshot serialization, Next.js, and hydration.
- [Preparation `/hast`](/api/prepare.md): plugins, resolvers, policy, and restore.
---
url: /guide/usage.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# Basic Usage
There are two ways to use the library: the source component for direct rendering, and the explicit pipeline that separates parsing, preparation, and rendering for servers, caches, workers, and async work. See [How it works](/guide/concepts.md) for how the outputs and entries relate.
## Source component
```tsx
import { Wikitext } from "react-wikitext";
import "react-wikitext/styles.css";
export function Article({ source }: { source: string }) {
return (
);
}
```
`Wikitext` performs the following synchronously:
1. Parses the source with `parseWikitext` or the supplied `parser`.
2. Clones the semantic tree, runs plugins, generates HAST, and sanitizes it with `prepareWikitext`.
3. Renders with `WikitextView`.
It memoizes parsing and preparation with `[parser, source, parseOptions]` and `[document, prepareOptions]`. Inline option objects cause a reparse on every render; keep options stable or wrap them in `useMemo`.
:::warning
The root entry only uses the synchronous prepare path. Async plugins in `wikitextPlugins` / `rehypePlugins` make `Wikitext` throw `ASYNC_PLUGIN_IN_SYNC_PATH`. Use the explicit pipeline for async plugins.
:::
## Explicit pipeline
```tsx
import { createWikiparser } from "react-wikitext/parse";
import { prepareWikitext } from "react-wikitext/hast";
import { WikitextView } from "react-wikitext/view";
import "react-wikitext/styles.css";
const parser = createWikiparser();
export function renderArticle(source: string) {
const document = parser.parse(source, { pageName: "Example" });
const prepared = prepareWikitext(document, {
headingIdStrategy: "instance",
});
return ;
}
```
Parsing and preparation can happen outside React. `prepareWikitext` does not modify the original parse snapshot; plugins run on a cloned semantic tree.
Create the parser once at module scope and reuse it. Its config is fixed at creation; changing site config requires a new parser:
```ts
import { createWikiparser } from "react-wikitext/parse";
import { MEDIAWIKI_CONFIG } from "react-wikitext/configs";
export const parser = createWikiparser({
config: MEDIAWIKI_CONFIG,
defaults: { pageName: "Example" },
});
```
## Reusing prepared output
Preparation reruns plugins, HAST conversion, and sanitization. When only presentation components change, reuse the existing render snapshot:
```tsx
import type { WikitextRenderDocument } from "react-wikitext/hast";
import { WikitextView } from "react-wikitext/view";
function Article({ prepared }: { prepared: WikitextRenderDocument }) {
return (
{displayName},
}}
/>
);
}
```
- A parse cache key should include the source, `pageName`, and site config.
- A render cache key also includes resolvers, plugins, `policy`, `labels`, and `headingIdStrategy`.
- Caching and invalidation are covered in [Performance and budgets](/guide/performance.md).
## Async plugins
The synchronous entry rejects async plugins. Use `prepareWikitextAsync` when a transformation is asynchronous:
```tsx
import { prepareWikitextAsync } from "react-wikitext/hast";
const prepared = await prepareWikitextAsync(document, {
rehypePlugins: [
() => async (tree) => {
// Fetch data or rewrite HAST asynchronously
return tree;
},
],
});
```
Preparation must finish before React renders; `WikitextView` never waits for a Promise. For server scenarios, see [Server rendering](/guide/server-rendering.md).
## Component overrides
`Wikitext` and `WikitextView` accept `components` (by HTML tag) and `semanticComponents` (by wiki semantic type). Overrides are trusted code and are the right place to integrate a design system:
```tsx preview
import type { ReactNode } from "react";
import { Wikitext } from "react-wikitext";
import "react-wikitext/styles.css";
const source = `
This quote uses a custom component.
{{Infobox|title=Template badge}}`;
function Quote({ children }: { children?: ReactNode }) {
return (
{children}
);
}
export default function Demo() {
return (
(
{displayName}
),
}}
/>
);
}
```
See the [React component API](/api/react.md) for the full mapping and props; styling, labels, and heading anchors are in [Styling and components](/guide/customization.md).
## Working with AI
This docs site is built with `llms` support: besides the copy-Markdown and open-in-ChatGPT/Claude entries under each page title, two plain-text files for LLMs are published alongside the site on every build:
- [llms.txt](https://react-wikitext.pages.dev/llms.txt): an index file with descriptions and links for every page on the site
```
https://react-wikitext.pages.dev/llms.txt
```
- [llms-full.txt](https://react-wikitext.pages.dev/llms-full.txt): the entire English site content merged into one file
```
https://react-wikitext.pages.dev/llms-full.txt
```
The Chinese site publishes the same files under `/zh/`: `/zh/llms.txt` and `/zh/llms-full.txt`. When citing a single page, appending `.md` to its URL also gives you the plain-Markdown version of that page.
Copy whichever link fits your use case and hand it to your agent.
## Related
- [Server rendering](/guide/server-rendering.md): server preparation, serialization, Next.js, and hydration.
- [Performance and budgets](/guide/performance.md): budgets, workers, and caching.
- [Troubleshooting](/guide/troubleshooting.md): `ASYNC_PLUGIN_IN_SYNC_PATH` and other common errors.
---
url: /guide/server-rendering.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# Server Rendering
Parsing and preparation do not depend on React, so they can run on the server, at build time, or in a worker. The client only loads `react-wikitext/view` and restores a render snapshot.
## Two deployment shapes
| Shape | Server does | Client loads | Fits |
| --------------------------- | ------------------------- | --------------------------- | ------------------------------ |
| Render source on the client | Sends source text only | Root entry (parser + React) | Small content, interactive |
| Prepare on the server | Parse, prepare, serialize | `/view` + restore function | Stable content, smaller client |
The second shape keeps parsing cost on the server; the client no longer loads `wikiparser-node`.
## Prepare on the server
```tsx
// server.tsx
import { createWikiparser } from "react-wikitext/parse";
import { prepareWikitext, serializeWikitextRenderDocument } from "react-wikitext/hast";
const parser = createWikiparser();
export function preparePayload(source: string, pageName: string) {
const document = parser.parse(source, { pageName });
const prepared = prepareWikitext(document);
return serializeWikitextRenderDocument(prepared);
}
```
```tsx
// client.tsx
"use client";
import { useMemo } from "react";
import { restoreWikitextRenderDocument } from "react-wikitext/hast";
import { WikitextView } from "react-wikitext/view";
export function Article({ payload }: { payload: unknown }) {
const prepared = useMemo(() => restoreWikitextRenderDocument(payload), [payload]);
return ;
}
```
`restoreWikitextRenderDocument` revalidates schema versions, the root, HAST structure, and the node budget, then reruns sanitization and finalization. It does not trust arbitrary third-party HAST; only restore snapshots from a trusted server or cache.
:::note
A snapshot contains the semantic tree, HAST, and source, so it is noticeably larger than plain HTML. Prepare large documents per page or per block, and budget the transfer.
:::
## Next.js App Router
The root entry and `/view` create Context at module load and use React hooks, so they only run in Client Components. Both entries ship a `"use client"` directive, so the host does not need to create the boundary; the server only uses `/parse` and `/hast`.
```tsx
// app/wiki/[title]/page.tsx
import { createWikiparser } from "react-wikitext/parse";
import { prepareWikitext, serializeWikitextRenderDocument } from "react-wikitext/hast";
import { ArticleView } from "./article-view";
import { loadPageSource } from "@/lib/wiki";
const parser = createWikiparser();
export default async function Page({ params }: { params: Promise<{ title: string }> }) {
const { title } = await params;
const source = await loadPageSource(title);
const document = parser.parse(source, { pageName: title });
const payload = serializeWikitextRenderDocument(prepareWikitext(document));
return ;
}
```
```tsx
// app/wiki/[title]/article-view.tsx
"use client";
import { useMemo } from "react";
import { restoreWikitextRenderDocument } from "react-wikitext/hast";
import { WikitextView } from "react-wikitext/view";
export function ArticleView({ payload, lang }: { payload: unknown; lang: string }) {
const prepared = useMemo(() => restoreWikitextRenderDocument(payload), [payload]);
return ;
}
```
```tsx
// app/layout.tsx
import "react-wikitext/styles.css";
```
- The server preparation step only calls `/parse` and `/hast`; neither entry loads React.
- Client Components restore and render; they never parse again.
- The root entry and `/view` ship `"use client"`, so Next.js treats them as client boundaries: they can be rendered from a Server Component, but parsing then happens on the client. When parsing must stay on the server, prepare the snapshot in the page component shown above.
- In other SSR setups, render `WikitextView` with `renderToStaticMarkup` and hydrate from the same snapshot on the client.
## Vite and other SSR setups
The idea is the same: prepare the snapshot on the server, send it as page data, and restore it on the client with `/view`.
```ts
// server entry
const payload = serializeWikitextRenderDocument(prepareWikitext(document));
const html = template.replace("__WIKITEXT__", JSON.stringify(payload).replace(/` cannot terminate the script early.
- If the page must expose content to crawlers, render `WikitextView` on the server too; the client must hydrate the same snapshot and tree.
## Hydration and anchors
- `headingIdStrategy: "instance"` uses a `useId` prefix; it matches when the server and client render the same tree.
- If the trees differ, or a page has multiple instances, pass an explicit `idPrefix`.
- Use `headingIdStrategy: "mediawiki"` when the host already links to bare anchors such as `#Section`; it assumes a single instance per page.
- `headingIdStrategy: "none"` writes no ids and generates no TOC links, which suits embedded content without anchors.
## Caching
Render snapshots are cacheable, but the key must cover every input that affects output:
- `AST_SCHEMA_VERSION`, `RENDER_SCHEMA_VERSION`, and the parser version.
- Source and `pageName`.
- Site config (`config`).
- `prepareOptions`: resolvers, plugins, `policy`, `labels`, `headingIdStrategy`, and `handlers`.
Restore rejects snapshots with a mismatched version; invalidate the cache when upgrading the library or parser. Parse caching is covered in [Performance and budgets](/guide/performance.md).
## Related
- [Basic usage](/guide/usage.md): source component and explicit pipeline.
- [Preparation `/hast`](/api/prepare.md): serialization, restore, and budgets.
- [Security model](/guide/security.md): the snapshot trust boundary.
---
url: /guide/rendering.md
---
> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.
# Rendering Behavior
This page describes what the default pipeline **actually outputs**. The library does not infer semantics for appearance: structures that cannot be rendered safely or correctly become source cards or explicit placeholders. To look up a single construct, see [Syntax support](/guide/syntax-support.md).
## Paragraphs, text, and formatting
- Consecutive inline content becomes `
`; two or more blank lines split paragraphs, and a single newline collapses to a space.
- Bold and italic come from MediaWiki quote syntax and render as `` / ``. When both are open, the output is `…`.
- Headings render as real `
`–`
` and participate in anchors and the table of contents.
- Horizontal rules render as ``.
- Comments, categories, and behavior switches do not enter the body. Categories go to the footer; behavior switches only affect the table of contents.
## Lists
- `*`, `#`, `;`, and `:` prefixes produce nested `
`, ``, and `
/
/
`.
- Continuation lines join the current list item; blank lines, headings, tables, and block HTML end a list.
- Definition lists support both `; term : definition` and `;term:definition`.
- List scanning inside a table cell is bounded by the cell range and never consumes the next cell.
## Tables
- The output order is `
…
…`; cells are `
` / `
`.
- Table, row, and cell attributes are kept through a whitelist; `style` keeps only a safe layout subset, while event attributes and `id` are always dropped.
- Layout is computed on a logical grid: `colspan` / `rowspan` are clamped to the table bounds, and missing slots get filler cells (`wtr-table__cell--filler`).
- **Nested wikitext tables** degrade to a source card. Nested HTML `