For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /guide/performance.md.
  • English
  • Performance and Budgets

    The default pipeline is synchronous: parsing and preparation run during the call. The library ships no scheduler, worker, or virtual list; scheduling belongs to the host. This page lists the knobs that exist.

    Where the cost goes

    StageMain costCan it be avoided?
    ParsingUpstream tokenization, syntax tree, semantic normalizationNo; cache it or move it to a worker
    PreparationTree clone, plugins, HAST conversion, sanitize, finalizeReuse the render snapshot when only presentation changes
    RenderingSemantic/HAST indexes, per-block rendering and boundariesNo; large documents need host pagination

    retainSyntax: true keeps the source syntax tree, which significantly increases memory and build cost. Enable it only when you need source positions or the playground syntax tree.

    Budgets

    Parsing

    OptionDefaultMeaning
    maxSourceLength100,000Source length after LF normalization
    maxSyntaxNodes50,000Syntax tree node count
    maxSemanticNodes50,000Semantic AST node count
    maxDepth1,000Syntax and semantic depth

    Effective limits merge as "per-call options → factory defaults → shared defaults". Invalid values throw INVALID_CONFIG; overflow throws SOURCE_TOO_LONG or SOURCE_TOO_COMPLEX.

    Preparation

    The working tree and final HAST use fixed budgets. They cannot be changed through parse options:

    BudgetLimit
    Semantic work nodes50,000
    Semantic work depth1,000
    HAST nodes50,000
    HAST depth1,000

    These are guardrails, not performance targets. Preparation fails when they are exceeded; any valid snapshot the library produces can be restored by restoreWikitextRenderDocument.

    Template expansion

    OptionDefault
    maxDepth40
    maxInvocations1,000
    maxExpandedLengthSame as the source limit (100,000)

    Parse in a worker

    There is no built-in worker, but snapshots are plain data and can cross threads:

    // worker.ts
    import { parseWikitext } from "react-wikitext/parse";
    import { serializeWikitextDocument } from "react-wikitext/ast";
    
    self.onmessage = (event: MessageEvent<string>) => {
      const document = parseWikitext(event.data, { pageName: "Example" });
      self.postMessage(serializeWikitextDocument(document));
    };
    // main.ts
    import { restoreWikitextDocument } from "react-wikitext/ast";
    import { prepareWikitext } from "react-wikitext/hast";
    
    const document = restoreWikitextDocument(payload);
    const prepared = prepareWikitext(document);
    • Preparation can also run in the worker; rendering stays on the main thread.
    • Check snapshot versions before transferring; rely on restore validation after a version change.
    • When the same source is requested repeatedly, caching the snapshot is cheaper than parsing again.

    Caching

    A cache key must cover every input that affects output:

    CacheKey
    Parse snapshotSource, pageName, site config, parse options, parser version
    Render snapshotParse key + resolvers, plugins, policy, labels, headingIdStrategy, handlers

    AST_SCHEMA_VERSION, SYNTAX_SCHEMA_VERSION, and RENDER_SCHEMA_VERSION changes are structural breaks; invalidate the whole cache. See Security model for the restore trust boundary.

    Avoid repeated work

    • Keep parseOptions and prepareOptions outside the component, or stabilize them with useMemo; inline objects reparse on every render.
    • Create the parser once at module load; create a new one when the config changes.
    • Reuse the render snapshot when only components or styles change.
    • Disable retainSyntax when source positions are not needed.
    • Prepare on the server and render on the client to cut browser download and execution; see Server rendering.

    Bundle by entry

    EntryParserReactTypical use
    Root entryYesYesClient-side source rendering
    /parseYesNoParsing, workers, build time
    /expandYesNoTemplate expansion
    /viewNoYesRender a prepared snapshot
    /hastNoNoPreparation and serialization
    /astNoNoAST queries and data work

    For display-only clients, load /view dynamically so the parser stays out of the initial bundle:

    async function loadViewer() {
      const [{ WikitextView }, { restoreWikitextRenderDocument }] = await Promise.all([
        import("react-wikitext/view"),
        import("react-wikitext/hast"),
      ]);
      return { WikitextView, restoreWikitextRenderDocument };
    }

    Large documents

    • Dense text around 100 KB may block the main thread; prefer a worker or page-split requests.
    • There is no virtual list; paginate, collapse, or chunk in the host.
    • Error boundaries isolate failed blocks but do not reduce total rendering cost.
    • The TOC, references, and categories are document-level structures and require the whole document to be prepared.

    Measuring

    Numbers vary with source, config, browser, and hardware, so this documentation does not pin them. Run pnpm bench in the repository and record the input, parser version, and before/after difference.