mdast-util-to-string
Get the plain-text content of any mdast node in a single function call.
Repository Health
Technical Analysis
mdast-util-to-string is a tiny utility from the unified/syntax-tree ecosystem that extracts the textual content of an mdast (Markdown Abstract Syntax Tree) node. Given a heading, paragraph, or any other node — or an array of nodes — it walks the node’s plain-text fields and children, unwrapping emphasis, strong, links, and other inline formatting into a single flat string.
It intentionally does one job well: it does not serialize back to Markdown (that’s mdast-util-to-markdown’s job) and has no notion of rendering — only of collapsing a tree into text. It’s the mdast counterpart to hast-util-to-string/hast-util-to-text for HTML ASTs, and is a common building block behind slug generation, search indexing, table-of-contents extraction, and word counts in remark-based tooling.
What You Get
- A single
toString(value, options)export with no default export — small, tree-shakeable surface area - Recursive serialization that handles single nodes, node lists, and deeply nested children uniformly
- Configurable
includeImageAltandincludeHtmloptions to control whether image alt text and raw HTML nodes contribute to the output - Full TypeScript types generated from JSDoc, with the
Optionstype exported for consumers - Zero runtime dependencies beyond the type-only
@types/mdastpackage - Battle-tested inside the wider unified/remark/rehype toolchain, so behavior matches what remark-based tools already expect
Common Use Cases
- Generating heading slugs or anchor text from a heading node’s rendered content
- Building a table of contents by extracting plain text from heading nodes across a document
- Feeding markdown content into full-text search indexes that expect plain strings, not markup
- Computing word counts or reading-time estimates from parsed Markdown/MDX content
- Stripping formatting from a node for use in meta descriptions, previews, or notifications
Under The Hood
Architecture
The entire implementation lives in one small module (lib/index.js) built around three tiny functions: one (serialize a single value), all (join serialized results across a list), and node (a type guard checking whether a value looks like an mdast node). toString normalizes options and delegates to one, which prefers a node’s own value field, then an alt field (for images, when enabled), then recurses into children via all, and falls through to an empty string otherwise. There is no class hierarchy, no internal state, and no I/O — just a pure recursive walk — which keeps the module trivial to reason about and reuse as a building block inside larger unified/remark-util packages, mirroring the same one/all recursion shape used by its hast-util sibling packages.
Tech Stack
The package is plain ESM JavaScript ("type": "module"), typed entirely through JSDoc comments and compiled to declaration files via tsc --build with strict mode and 100% type-coverage enforced at build time. Its only runtime dependency is the type-only @types/mdast package. Linting and formatting run through xo (wrapping ESLint) and prettier, with remark-preset-wooorm used to lint the README itself, and coverage is measured with c8. CI runs on GitHub Actions across the current and LTS Node.js releases.
Code Quality
test.js uses Node’s built-in node:test and node:assert/strict — no external test framework — and asserts both the exported public API surface and behavior across every branch: missing values, null, valueless nodes, value-over-alt-over-children precedence, includeImageAlt/includeHtml toggles, and list serialization. The test-coverage script enforces c8 --100, meaning the suite is required to hit 100% statement/branch coverage on every release. Combined with type-coverage’s 100% requirement and xo’s strict lint rules, the project holds itself to an unusually rigorous bar for a package this small.
API Design
The public surface is a single named export, toString, taking one required argument and one optional options object with two well-named boolean flags — there is no setup, no configuration object to construct, and no boilerplate beyond installing the package and importing the function. Naming and behavior are consistent with sibling utilities across the syntax-tree organization (hast-util-to-string, hast-util-to-text), so developers already familiar with one unified/rehype/remark utility can predict this one’s behavior without reading the docs, though the README’s API section is thorough enough that reading isn’t strictly necessary either.
Used by 7 apps in this directory
cocoindex
Data Engineering · AI Development
An incremental data indexing engine that keeps AI agent context perpetually fresh by reprocessing only what changed.
likec4
Developer Tools · Devops
Define your software architecture as code and get always up-to-date, interactive C4 diagrams generated automatically from a DSL.
Rocket.Chat
Team Chat
The secure, self-hosted team communications platform for organizations that cannot compromise on data sovereignty.
Supabase
Developer Tools · Databases · Search
The open-source Postgres development platform that replaces Firebase with authentication, real-time APIs, edge functions, storage, and vector embeddings — all built on PostgreSQL.
superset
AI Code Assistants · AI Development
Orchestrate an army of AI coding agents—Claude Code, Codex, Gemini CLI, and more—running simultaneously in isolated git worktrees from a single Electron desktop app.
Webstudio
Design Tools · No Code Platforms
Open source visual development platform with complete CSS control, headless CMS integration, and full infrastructure ownership
Zulip
Team Chat
Topic-based team chat that brings the structure of email threads to real-time messaging, so distributed teams never lose context across hundreds of concurrent conversations.