Spec-compliant YAML parsing, out to give JSON.parse a run for its money.
⚡ ~4× faster and ~1–3× lighter than js-yaml (bigger file, bigger gap) — with near-JSON.parse memory even at 10 MB. See the benchmarks ↓
lightning-yaml is a pure-JS YAML 1.2 parser and stringifier that parses and
writes at speeds approaching native JSON.parse/JSON.stringify — while faithfully
implementing YAML 1.2, passing ~97.6% (364/373) of the official
yaml-test-suite. It's an API-level drop-in for either —
same exports and call signatures, ESM + CJS + full TypeScript types, and zero
runtime dependencies. No more trading YAML's readability for JSON's performance.
Two goals, in priority order: (1) full YAML 1.2 spec compliance, then
(2) speed and memory within reach of native JSON.parse/JSON.stringify.
Everything else is secondary to those two.
- Fast. Parses and stringifies at speeds approaching native
JSON.parse/JSON.stringify— ~4× faster than js-yaml on parse across our benchmark workloads. - Spec-compliant. Faithfully implements YAML 1.2 — passes ~97.6% (364/373) of the official yaml-test-suite.
- Drop-in (API-level). Same exports and signatures as
yamlandjs-yaml— swap the import and your code runs. An option we don't yet honour (schema,sortKeys,indent, …) throws a clear error rather than silently changing your output; see Drop-in. - Lean. Zero runtime dependencies, small bundle; ships ESM + CJS + full TypeScript types.
- Complete. Full YAML 1.2 core — flow & block syntax, anchors/aliases, tags
incl.
!!binary, multi-document streams, and more.
Measured primarily against native JSON.parse — the bar this project holds
itself to — with js-yaml and yaml for context. Representative figures on the
maintainer's machine; the full tables (all datasets, every parser, tracked over
time) live at lightning-yaml.dev, reading published
runs from the orphan benchmark-data branch.
| Representative metric | JSON.parse |
lightning-yaml | js-yaml | yaml |
|---|---|---|---|---|
Parse — large records (× JSON.parse) |
1.0× | 2.1× | 8.9× | 107× |
Peak RSS — 10 MB doc (× JSON.parse) |
1.0× | 1.4× | 2.9× | 6.9× |
| Bundle — minified / gzip | native | 40 KB / 12 KB | 52/16 KB | 96/29 KB |
Speed and memory are shown as ratios to JSON.parse — absolute ms/MB drift with the machine, but the relative cost doesn't (and peak memory is the stable figure). Numbers are from the newest run on the benchmark-data branch — commit 4ca6140 (2026-07-23); js-yaml 5.2.1, yaml 2.9.0. Bundle sizes are deterministic.
So lightning-yaml stays within a small multiple of JSON.parse, where js-yaml and
yaml cost several to a hundred times more.
Full benchmarks (all datasets, every parser) → lightning-yaml.dev
pnpm add lightning-yaml
npm install lightning-yaml
yarn add lightning-yaml
bun add lightning-yamlBrowser / CDN — no build step required:
<script src="https://cdn.jsdelivr.net/npm/lightning-yaml/dist/lightning-yaml.min.js"></script>
<script>
const data = YAML.parse('greeting: hello');
</script>Or as modern ESM, straight from a CDN:
import { parse } from 'https://cdn.jsdelivr.net/npm/lightning-yaml/+esm';ESM, CommonJS, and TypeScript types all ship in the box.
import { parse, parseAll, stringify } from 'lightning-yaml';
parse(`
name: lightning-yaml
version: 0.1.0
features: [fast, spec-compliant, drop-in]
`);
// → {
// name: 'lightning-yaml',
// version: '0.1.0',
// features: ['fast', 'spec-compliant', 'drop-in'],
// }
stringify({ hello: 'world', list: [1, 2, 3] });
// → hello: world
// list:
// - 1
// - 2
// - 3
parseAll(`
---
a: 1
---
b: 2
`);
// → [{ a: 1 }, { b: 2 }] ← multi-document streamsFull API reference and function signatures → lightning-yaml.dev
Already using another YAML library? Swap one import, keep your code.
// Coming from js-yaml — comment out the old import:
// import { load, dump } from 'js-yaml';
import { load, dump } from 'lightning-yaml/js-yaml';
// Using the `yaml` library — same idea:
// import { parse, stringify } from 'yaml';
import { parse, stringify } from 'lightning-yaml/yaml';Status — surface-level today. The shims are a TypeScript drop-in (same exports and signatures), so your code compiles and runs — but an option we don't yet honour (
schema,sortKeys,indent, …) throws instead of silently leaving your output unchanged, so you find out at the call site (a boolean flag left at the value lightning-yaml already produces — usuallyfalse— still works). Full option compatibility is the goal; each shim's option-support matrix (js-yaml, yaml) lists which options are easy or hard to support next.
In order:
- Compliance with the YAML 1.2 specification. Correctness comes first,
always — a fast parser that mis-reads your config is worthless. Where the spec
itself is unclear, the yaml-test-suite
is our north star; where even that is ambiguous, we fall back to matching the
behaviour of the
yamland js-yaml libraries. By defaultparseis lenient about a handful of spec-invalid inputs real-world YAML sometimes contains (see Decisions and deviations); pass{ strict: true }for full YAML 1.2.2 rejection of those inputs too. - Speed and memory within reach of native
JSON.parse/JSON.stringify. We'll never match native byte-for-byte, and we know it — but we treat that gap as a bug to shrink, chasing every last nanosecond so there's no performance reason left to reach for JSON over YAML.
Any sanctioned departure from these goals — anywhere lightning-yaml knowingly
differs from the spec or from js-yaml / yaml — is tracked in
Decisions and deviations below.
lightning-yaml is young, and the single most useful thing you can do is try it and tell me what happens — I'm hugely grateful to anyone who gives it a run.
- Try to break it. Find some YAML that crashes the parser, or that it reads in a way the YAML 1.2 spec doesn't? I'd be thrilled to hear about it — open an issue with the input and I'll be on it fast. Real-world edge cases are exactly what make this library better.
- Slower than you expected? If lightning-yaml is slower than another YAML parser on your data, slower than the benchmarks suggest, or just doesn't work in your environment, please open an issue — I'd genuinely love to dig into it.
- Spot a claim that doesn't hold up? If a benchmark looks wrong, a number or
claim reads as inaccurate, or we're configuring js-yaml or
yamlin a way that's unfair to them — where a fairer setup would make them look faster — I want to know. Open an issue: benchmark honesty and accuracy matter more here than looking good, and I'll fix anything that's off. - Ideas, questions, or just want to chat? GitHub Discussions is the place for anything that would make the library better.
I care a lot about making this the best YAML parser it can be, and I'll move quickly on whatever you find.
Sending a pull request? See CONTRIBUTING.md — in short,
if you change anything under src/, run pnpm changeset to declare the version
bump; CI checks for it.
YAML 1.2 core, feature-complete but for one deliberate gap — merge keys
(<<). Today a << key is read as an ordinary string key rather than being
merged (it's neither expanded nor rejected); everything else in the 1.2 core is
implemented.
lightning-yaml passes ~97.6% of the official yaml-test-suite (364/373) — a
faithful implementation of YAML 1.2 core. The handful of remaining failures
are unrelated spec edge cases, not merge keys (the suite doesn't exercise <<).
The single, authoritative list of places lightning-yaml knowingly departs
from the YAML 1.2 spec, or from how js-yaml / yaml behave — kept here, in the
open, so every exception stays visible rather than buried in a code comment. If
lightning-yaml differs from the spec or another library in a way not listed
here, treat it as a bug and
open an issue.
- Lenient by default: a tab used as block indentation is tolerated, not
rejected. YAML 1.2.2 §6.1
forbids tabs in a block collection's indentation, but real-world YAML sometimes
has them, so
parse/parseAllaccept it by default. Pass{ strict: true }to reject that input instead, matching the spec (and theyaml/js-yaml libraries) exactly:const withTab = "a:\n\tb: 1\n"; // a tab indenting "b" parse(withTab); // { a: { b: 1 } } — accepted by default parse(withTab, { strict: true }); // throws: spec-invalid indentation
- Duplicate mapping keys: last one wins. The spec makes a duplicate key an
error; we keep the last instead, matching
JSON.parse—{a: 1, a: 2}parses to{a: 2}. - Merge keys (
<<) are read as an ordinary key, not merged.<<: *anchorgives you a literal"<<"string key rather than merging the aliased map in (js-yaml andyamlmerge it). It's neither expanded nor rejected today. - Compat options that aren't implemented yet throw. The
lightning-yaml/js-yamlandlightning-yaml/yamlshims take the same options (schema,sortKeys,indent, …) so your code compiles, but an option we can't yet honour throws a clear error naming it — rather than silently leaving the output unchanged. A boolean flag left at the value lightning-yaml already produces (e.g.mapAsMap: false) still works; any other value throws. This coversyaml'sJSON.stringify-style positional indent shorthand too —stringify(value, 4)orstringify(value, replacer, 4)— where a number or string throws even for a width realyamlwould quietly clamp to its own default rather than honour — e.g. a negative number. - YAML 1.2 core schema, not 1.1. Plain
yes/no/on/offstay strings (not booleans) and there are no base-60 numbers, so results can differ from js-yaml's 1.1-flavoured default. YAML 1.1 is a non-goal. - Implicit flow-collection keys are rejected. Input like
{[1, 2]: v}— a sequence used as a mapping key — is an error here, matching the spec; theyamllibrary accepts it, and we deliberately don't. - Malformed directives are rejected, where
yamlis lenient. A repeated%TAGfor the same handle, and a%YAMLdirective with a higher major version (e.g.%YAML 2.0), are errors here — matching the spec (§6.8.2 / §6.8.1) and js-yaml. Theyamllibrary instead keeps the last%TAGand only warns on the version (still parsing); we deliberately reject both.
lightning-yaml is built with the help of Claude Code, but every commit is human-reviewed and I'm accountable for all of the code. The aim is the leverage of AI without a vibe-coded, unreviewed repo — the assistant helps write it, a human owns it. You'll also see a steady stream of commits that do nothing but tidy up: keeping the code human-readable and maintainable is an ongoing priority, not an afterthought.
Apache License 2.0 — © 2026 Joseph Siddall.
How lightning-yaml is built, measured, and tested — the benchmark methodology, the separate peak-memory harness, the consistency suite, and the parser internals — lives in the docs, so this README stays focused on using the library:
- lightning-yaml.dev → — guides, benchmarks, the full API reference, and the design write-ups.
- Try it live → — paste in YAML or
JSON and see what lightning-yaml produces, side by side with js-yaml and
yaml.