mrkeyoor.com_
Wed 05 Aug 19:54 UTC
npmUtilsupdated 05 Aug 2026

yaml

yaml is a full YAML parser and stringifier for JavaScript, written by Eemeli Aro. At the surface it works like JSON: parse(str) gives you plain objects, stringify(value) gives you YAML text. Underneath there are two more layers most libraries do not have: a Document API that keeps comments, blank lines, and anchors intact while you edit values, and a raw lexer/parser/composer for building tools like linters and formatters. It supports both YAML 1.1 and 1.2, passes the official yaml-test-suite, has zero dependencies, and runs in Node and browsers. It is what Prettier and a large slice of the config-tooling ecosystem parse YAML with.

Verdict

The most spec-correct YAML library in JavaScript and the only good choice when you must round-trip files with comments intact. If all you do is read a trusted config once, js-yaml is lighter and equally fine.

API stability4/5v2 has been the stable line since 2020 and the README commits to semver for all documented APIs; the pending v3 on the next tag means a breaking major is coming, so pin accordingly.
Docs4/5eemeli.org/yaml documents all three API layers with examples, but the layering itself makes the docs a maze for someone who just wants parse and stringify.
Maintenance4/5Pushed August 2026 with 35 open issues and PRs and a v3 in active prerelease; the caveat is that it is essentially a single-maintainer project.
Ecosystem5/5Roughly 183M weekly downloads with zero dependencies of its own; it is the YAML engine inside Prettier and much of the modern config-tooling stack.

Use it if

  • You need to edit a YAML file and write it back without destroying comments and formatting: parseDocument plus setIn is the only mainstream JS option that does this well
  • You care about spec correctness, like multi-document streams, anchors, merge keys, or the differences between YAML 1.1 and 1.2, because this library passes the full yaml-test-suite
  • You are building YAML tooling (linter, language server, formatter) and need access to tokens and the concrete syntax tree, not just the parsed values
  • You want zero dependencies and dual CJS/ESM support so it drops into any build without module-format drama
Skip it if

Setup reality

npm install yaml and you are done: zero dependencies, bundled TypeScript types, works from require() and import both. The friction is conceptual rather than mechanical. The three API layers (parse/stringify, Document, Lexer/Parser) confuse newcomers who land on the AST docs when they just wanted parse. The YAML 1.2 default bites anyone with legacy files full of yes/no booleans or sexagesimal numbers. And the included types currently target TypeScript 5.9, so older TS setups may need skipLibCheck. A v3 is published on the next dist-tag, so double-check which major your lockfile resolves before reading docs.

Patterns

Parse and stringify like JSONparse-and-stringify

import { parse, stringify } from 'yaml'

const data = parse('a: 1\nlist:\n  - x\n  - y\n')
// { a: 1, list: ['x', 'y'] }

const text = stringify({ number: 3, block: 'two\nlines\n' })
// number: 3
// block: |
//   two
//   lines

parse throws on invalid YAML. If you want errors collected instead of thrown, use parseDocument and check doc.errors.

Load a YAML config fileread-config-file

import fs from 'node:fs'
import { parse } from 'yaml'

const config = parse(fs.readFileSync('./config.yml', 'utf8'))
console.log(config.server.port)

There are no file helpers by design; the library also runs in browsers, so reading the file stays on your side.

Control indentation and line wrappingstringify-formatting

import { stringify } from 'yaml'

stringify(data, {
  indent: 4,
  lineWidth: 0, // disable line folding entirely
  defaultStringType: 'QUOTE_DOUBLE'
})

The default lineWidth of 80 folds long strings across lines, which surprises people diffing generated files; 0 turns folding off.

Edit a file without losing commentspreserve-comments

import fs from 'node:fs'
import { parseDocument } from 'yaml'

const doc = parseDocument(fs.readFileSync('app.yml', 'utf8'))
doc.setIn(['server', 'port'], 8080)
fs.writeFileSync('app.yml', String(doc))

This is the feature that justifies the library: comments, blank lines, and anchors survive the round trip. Plain parse/stringify discards them.

Parse a multi-document streammulti-document-streams

import { parseAllDocuments } from 'yaml'

const src = 'a: 1\n---\nb: 2\n---\nc: 3\n'
for (const doc of parseAllDocuments(src)) {
  console.log(doc.toJS())
}

Kubernetes manifests and CI files often concatenate documents with ---; parse only reads a single document and errors on the rest.

Collect errors instead of throwingcollect-errors

import { parseDocument } from 'yaml'

const doc = parseDocument(userInput)
if (doc.errors.length > 0) {
  for (const err of doc.errors) {
    console.error(err.code, err.message, err.pos)
  }
} else {
  const value = doc.toJS()
}

Each error carries a code and character positions, which is what you want for showing squiggles in an editor or validating user uploads.

Handle YAML 1.1 files and merge keysyaml-1-1-merge-keys

import { parse } from 'yaml'

// full 1.1 semantics: yes/no become booleans, << merges work
parse(src, { version: '1.1' })

// stay on 1.2 but honor merge keys
parse(src, { merge: true })

The default is YAML 1.2, so yes/no are plain strings and << is ignored. This is the top porting gotcha from PyYAML or docker-compose-era files.

Walk and transform the ASTvisit-transform-nodes

import { parseDocument, visit } from 'yaml'

const doc = parseDocument(src)
visit(doc, {
  Scalar(key, node) {
    if (typeof node.value === 'string') {
      node.value = node.value.trim()
    }
  }
})
console.log(String(doc))

Return visit.REMOVE from a visitor to delete a node, or a replacement node to swap it; this is how linters and codemods are built on this library.

Restrict parsing to JSON-compatible valuesjson-compatible-strict-mode

import { parse } from 'yaml'

parse(src, { schema: 'json' })
// only JSON types allowed; 0x10, dates, and !!binary all fail

Useful when the output must survive JSON.stringify unchanged, like config that gets forwarded to another service.

Build a document with comments from scratchbuild-document-programmatically

import { Document } from 'yaml'

const doc = new Document({ name: 'svc', replicas: 2 })
doc.commentBefore = ' Generated file, do not edit'
doc.get('replicas', true).comment = ' scale with care'
console.log(String(doc))
// # Generated file, do not edit
// name: svc
// replicas: 2 # scale with care

get(key, true) returns the underlying node instead of the JS value; comments hang off nodes, not off plain values.

Create anchors and aliasesanchors-and-aliases

import { Document } from 'yaml'

const doc = new Document({ base: { retries: 3 }, job: {} })
const alias = doc.createAlias(doc.get('base', true), 'defaults')
doc.setIn(['job', 'config'], alias)
console.log(String(doc))
// base: &defaults
//   retries: 3
// job:
//   config: *defaults

Aliases are references to the same node, so later edits to the anchored node show up everywhere the alias resolves.

Alternatives

PackageRegistryPick it when
js-yamlnpmYou want the long-standing default with a smaller footprint and only need plain load/dump without comment preservation.
gray-matternpmYour actual task is parsing YAML front matter out of Markdown files, which it handles end to end.