unified

Content as structured data

We compile content to syntax trees and syntax trees to content.
We also provide hundreds of packages to work on the trees in between.
You can build on the unified collective to make all kinds of interesting things.

Learn

We provide the interface: for parsing, inspecting, transforming, and serializing content. You work on structured data. Learn how to plug building blocks together, write your own, and make things with unified.

  1. Intro to unified

    Guide that summarizes the what and why of unified

    1. guide
    2. introduction
    3. welcome
  2. Use unified

    Guide that delves into transforming markdown to HTML

    1. guide
    2. rehype
    3. remark
    4. transform
    5. use
  3. Tree traversal

    How to do tree traversal (also known as walking or visiting a tree)

    1. recipe
    2. traverse
    3. tree
    4. unist
    5. visit
    6. walk
  4. Find a node

    How to find a node in any unist syntax tree

    1. recipe
    2. find
    3. node
    4. traverse
    5. tree
    6. walk
  5. Support tables in remark

    How to support GitHub-style tables in remark (or react-markdown)

    1. recipe
    2. gfm
    3. github
    4. plugin
    5. remark
    6. table
  6. See 15 other articles

Sponsor

To support our efforts financially, sponsor us on GitHub, thanks.dev, or OpenCollective. This lets us spend more time maintaining our projects and developing new ones.

  1. Vercel

    Develop. Preview. Ship. – Creators of nextjs.org

  2. GatsbyJS

  3. Motif

    When content meets code, magic happens ✨

  4. HashiCorp

  5. GitBook

  6. See 98 other sponsors

Explore

The ever growing ecosystem that the unified collective provides today consists of 305 open source projects, with a combined 70k stars on GitHub. In comparison, the code that the collective maintains is about 104 Moby Dicks or 41 Linuxes. In the last 30 days, the 471 packages maintained in those projects were downloaded 9.6b times from npm. Much of this is maintained by our teams, yet others are provided by the community.

  1. unist-util-generated

    unist utility to check if a node is generated
    1. 81%
    2. 20m
  2. unist-util-visit-parents

    unist utility to recursively walk over nodes, with ancestral information
    1. 83%
    2. 237m
  3. @mdx-js/react

    React context for MDX
    1. 87%
    2. 82m
  4. unist-util-remove-position

    unist utility to remove positions from a tree
    1. 81%
    2. 65m
  5. retext-english

    retext plugin to parse English prose
    1. 74%
    2. 630k
  6. See 471 packages and 305 projects

Build

We provide the building blocks: from tiny, focussed, modular utilities to plugins that combine them to perform bigger tasks. And much, much more. You can build on unified, mixing and matching building blocks together, to make all kinds of interesting new things.

  1. Prettier

    Uses unified to format markdown

  2. Gatsby

    Uses unified to pull content into GraphQL

  3. Write Music

    Uses unified to visualize sentence length

  4. Node.js

    Uses unified to check and build their docs

  5. alex

    Uses unified to catch insensitive, inconsiderate writing

  6. See 21 other cases

Work

Maintaining the collective, developing new projects, keeping everything fast and secure, and helping users, is a lot of work. In total, we’ve closed 5k issues/PRs while 148 are currently open (3%).

  1. josemarluedke/remark-hbs@0.5.0·

    The first release since 2021. remark-hbs moves to ESM and unified 11, and the whole toolchain is modernized.

    The plugin's rendered output is unchanged. This release is a platform and packaging update, not a behaviour change — see What did not change for how that was verified.

    💥 Breaking changes

    ESM-only

    The package is now "type": "module" with a default export and an exports map. There is no CommonJS build.

    // Before
    const hbs = require('remark-hbs');
    
    // After
    import hbs from 'remark-hbs';
    

    CommonJS consumers are not shut out — require() of an ES module works on the Node versions this package supports, and returns a namespace with __esModule set, so a compiled-TypeScript require('remark-hbs').default keeps working. If you are on an older Node, use await import('remark-hbs').

    Because an exports map is now declared, deep imports into the package (remark-hbs/something) are no longer resolvable. Only the package root is public.

    Node 20.19+ required

    engines moves from >= 10.* to ^20.19.0 || >=22.12.0. Those specific floors are the Node versions that support require() of an ES module, which is what keeps CommonJS consumers working.

    unified 11 is now a peer dependency
    "peerDependencies": { "unified": "^11.0.0" }
    

    Install unified yourself. unified 9 and 10 are no longer supported — the plugin now uses unist-util-visit 5 and the unified 11 type surface.

    Types rewritten

    index.d.ts used export = with a namespace, which cannot describe an ESM default export. It is now a default export with RemarkHbsOptions as a named export:

    // Before
    import remarkHbs from 'remark-hbs';           // via esModuleInterop
    // (RemarkHbsOptions lived on a namespace)
    
    // After
    import remarkHbs, { type RemarkHbsOptions } from 'remark-hbs';
    

    The option names are unchanged — escapeCurliesCode and escapeCurliesInlineCode, both defaulting to true.

    unist-builder dropped

    It was only used to build html and text nodes, which are now plain object literals. The dependency is gone; unist-util-visit (^5.1.0) is the only runtime dependency.

    🐛 Fixes

    • Incompatible with Unified v10 (#27) — the old types imported Settings from unified, which unified 10 had removed, breaking type-checking with TS2305. unified 11 exports Settings again and the types now resolve cleanly.
    • Removed a latent ReferenceError: parseHBS contained a bare recursion = 0 assignment to an undeclared global, which throws under ESM's implicit strict mode.

    What did not change

    The point of this release was to modernize the platform without moving a single byte of rendered output, and that was verified rather than assumed:

    • A harness was pinned to the old stack (unified 9 / remark-html 13.0.1 / unist-util-visit 2 / unist-builder 2) and first confirmed to reproduce the previously committed snapshots exactly, establishing it as a trustworthy baseline.
    • Old and new were then diffed across every test input and both fixtures, under default options and with escapeCurliesCode: false / escapeCurliesInlineCode: false: byte-identical throughout.
    • Snapshots were migrated to vitest by decoding and comparing them key-by-key against that baseline, not by re-recording them.
    • Verified against a real consumer (Docfy): clean compile, unchanged test results, successful classic-Ember and Vite builds, and all 40 generated .gjs template files byte-identical by checksum.

    ⚠️ Note for remark-html users

    Unrelated to this release, but easy to trip over while upgrading: remark-html began sanitizing by default in 13.0.2, which strips the raw component invocations this plugin emits. If your components vanish from the output, pass sanitize: false:

    unified().use(markdown).use(hbs).use(html, { sanitize: false });
    

    Known limitations

    Unchanged from previous releases, and confirmed to behave identically:

    • An inline component invocation followed by more text drops the trailing text — Text with <MyComponent /> and {{foo}} loses and {{foo}} (see josemarluedke/docfy#150).
    • Closing an element tag on its own line is parsed as a blockquote by CommonMark (#17).

    🛠️ Internal

    • jest → vitest, which runs ESM natively rather than needing NODE_OPTIONS='--experimental-vm-modules'
    • yarn → pnpm, with a packageManager field
    • CI rebuilt: actions/checkout@v5 / actions/setup-node@v5 and a Node 20 / 22 / 24 matrix, replacing a global yarn install on Node 10
    • Added eslint (flat config) and a tsc --noEmit type check, both behind a single pnpm run lint; prettier 2 → 3
    • Dev tooling to current majors: eslint 10, TypeScript 7, vitest 4, globals 17
    • Added files and exports; removed the stale .npmignore

    🤝 Credits

    The ESM conversion began in #28 by @NullVoxPopuli back in December 2021 — that PR's ESM work, its .prettierrc.cjs rename, and its fix for the undeclared-global bug all carried into #35.


    💥 Breaking Change

    📦 Dependencies


    Contributors

    @NullVoxPopuli, @dependabot, @dependabot[bot] and @josemarluedke


    For full changes, see the comparison between v0.4.1 and v0.5.0

  2. jaywcjlove/rehype-video@2.3.2·

  3. inokawa/remark-pdf@0.2.5·

    What's Changed

    Full Changelog: https://github.com/inokawa/remark-pdf/compare/0.2.4...0.2.5

  4. Explore recent releases