The js-yaml, yaml, yaml-front-matter, and yamljs packages all handle YAML parsing in JavaScript, but they serve different architectural needs. js-yaml is the long-standing standard for general-purpose YAML processing with extensive customization. yaml is a modern, spec-compliant rewrite that prioritizes safety and supports both CommonJS and ESM natively. yaml-front-matter is a specialized utility for extracting metadata headers from Markdown files, commonly used in static site generators. yamljs is a legacy port of the original Python YAML library that is no longer recommended for new projects due to lack of maintenance and outdated patterns.
YAML is everywhere in frontend development — from configuration files (vite.config.yaml) to content metadata in Markdown blogs. But not all YAML parsers are built the same. Some prioritize speed, others safety, and a few solve very specific problems like extracting front-matter. Let's break down js-yaml, yaml, yaml-front-matter, and yamljs so you can pick the right tool without guesswork.
Security is the biggest differentiator. YAML can execute arbitrary code if parsed incorrectly (a risk known as "prototype pollution" or remote code execution).
js-yaml gives you power but requires discipline. It has two loaders: load (unsafe) and loadAll with SAFE_SCHEMA (safe). If you forget to specify the safe schema, you risk executing malicious code.
import yaml from 'js-yaml';
// ❌ DANGEROUS: Can execute arbitrary JS tags
const data = yaml.load(userInput);
// ✅ SAFE: Must explicitly request safe loading
const safeData = yaml.load(userInput, { schema: yaml.FAILSAFE_SCHEMA });
yaml is safe by default. It does not allow custom tags or types that could execute code unless you explicitly opt-in. This makes it much harder to accidentally introduce a security hole.
import { parse } from 'yaml';
// ✅ Always safe, no extra config needed
const data = parse(userInput);
// To allow custom tags, you must build a custom schema explicitly
// const doc = new YAML.Document({ customTags: ... });
yaml-front-matter relies on an underlying parser (often js-yaml internally). You must check its documentation to ensure it uses safe loading. In many versions, it delegates safely, but you are one dependency update away from potential risk if the maintainer changes behavior.
import frontMatter from 'yaml-front-matter';
// Extracts header safely if underlying lib is configured right
const { __content, ...header } = frontMatter.loadSync(markdownFile);
yamljs has known security issues and does not enforce safe loading patterns clearly. It was built in an era when these threats were less understood. Do not use it for untrusted input.
// ❌ AVOID: No clear safe/unsafe distinction in API
var data = YAML.load(userInput);
Modern frontend tooling (Vite, Rollup, Next.js App Router) expects ES Modules (ESM). Old CommonJS-only packages can cause build errors or require shims.
js-yaml supports ESM but started as a CommonJS library. It works in most bundlers now, but you might see warnings or need specific import paths depending on your setup.
// Works in most modern setups
import yaml from 'js-yaml';
yaml was built for the modern era. It ships with first-class ESM support, tree-shaking, and tiny bundle footprints. It integrates seamlessly with TypeScript and modern bundlers without configuration.
// Native ESM, perfect for Vite/Next.js
import { parse, stringify } from 'yaml';
yaml-front-matter is typically CommonJS. While it works via interoperability layers, it may cause "default export" confusion in strict ESM projects.
// Might require default import handling in strict ESM
import frontMatter from 'yaml-front-matter';
yamljs is strictly CommonJS and outdated. Using it in a modern ESM project often requires legacy shims or dynamic imports, adding unnecessary complexity.
// Legacy CommonJS only
const YAML = require('yamljs');
If you are building a blog, docs site, or CMS, you likely need to parse YAML headers inside Markdown files (the block between --- lines).
js-yaml does not do this out of the box. You must write your own regex to split the content, then pass the header to the parser. This is error-prone if headers vary.
import yaml from 'js-yaml';
const match = fileContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
if (!match) throw new Error('Invalid front-matter');
const header = yaml.load(match[1], { schema: yaml.FAILSAFE_SCHEMA });
const content = match[2];
yaml also requires manual splitting, though its cleaner API makes the subsequent parsing safer. You still need the regex step.
import { parse } from 'yaml';
const match = fileContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
const header = parse(match[1]); // Safe by default
const content = match[2];
yaml-front-matter solves this exact problem. It handles the splitting, parsing, and error handling in one call. It returns the content separately from the metadata.
import frontMatter from 'yaml-front-matter';
const { __content, title, date } = frontMatter.loadSync(fileContent);
// __content is the markdown body; title/date are from YAML header
yamljs has no built-in front-matter support. You would need to combine it with external string manipulation, offering no advantage over js-yaml here.
Real-world YAML often includes comments, circular references, or custom types (like dates or binary data).
js-yaml supports custom schemas extensively. You can define how specific tags (e.g., !!timestamp) are resolved. It handles circular references if configured, but the API is verbose.
// Custom schema example
const schema = yaml.DEFAULT_SCHEMA.extend({
implicit: [ /* custom types */ ]
});
const data = yaml.load(input, { schema });
yaml preserves comments in the AST (Abstract Syntax Tree). If you need to read a config file, modify it, and write it back without losing user comments, this is the only choice. It also handles circular references naturally.
import { parseDocument } from 'yaml';
const doc = parseDocument(input);
// Edit values...
doc.contents.items[0].value = 'new value';
// Comments remain intact when stringified
const output = doc.toString();
yaml-front-matter focuses only on extraction. It does not offer advanced AST manipulation or comment preservation. It is a "read-only" utility for metadata.
yamljs has limited type support and struggles with complex YAML features like anchors, aliases, or comments. It treats YAML as a simple data interchange format, ignoring many spec features.
While we aren't listing exact numbers, the architectural differences impact performance.
yaml is generally faster for parsing large documents due to modern optimization and avoids the overhead of legacy code paths.js-yaml is slightly heavier but stable. The performance gap is negligible for typical config files.yaml-front-matter adds a tiny overhead but saves developer time on regex logic.yamljs is inefficient by modern standards and parses slower on large datasets.| Feature | js-yaml | yaml | yaml-front-matter | yamljs |
|---|---|---|---|---|
| Safety | ⚠️ Manual (must choose safe schema) | ✅ Safe by default | ⚠️ Depends on internal lib | ❌ Unsafe/Legacy |
| ESM Support | ✅ Yes (hybrid) | ✅ Native/First-class | ⚠️ CommonJS focused | ❌ CommonJS only |
| Front-Matter | ❌ Manual regex needed | ❌ Manual regex needed | ✅ Built-in | ❌ Manual needed |
| Comment Preservation | ❌ No | ✅ Yes | ❌ No | ❌ No |
| Maintenance Status | ✅ Active | ✅ Active | ⚠️ Low activity | ❌ Unmaintained |
| Best For | Complex custom schemas | Modern apps, safety | Static sites, blogs | ❌ None (Legacy) |
yaml is the modern standard. If you are starting a new project today, especially in the frontend ecosystem, this should be your default choice. It is safe, fast, and respects the YAML spec fully.
js-yaml remains a strong contender if you need deep customization or are maintaining a legacy codebase that already depends on its specific schema features. Just remember to always use the safe loader.
yaml-front-matter is a specialist tool. Keep it in your toolkit for Markdown-heavy projects (like Gatsby, Next.js blogs, or documentation sites), but don't use it for general YAML parsing.
yamljs belongs in the past. It offers no advantages over the others and carries significant technical debt. If you see it in a codebase, plan a migration to yaml or js-yaml immediately.
Final Thought: YAML parsing seems simple until you deal with untrusted input or need to preserve comments. Choose the library that matches your risk tolerance and tooling requirements — don't let a legacy dependency compromise your build pipeline.
Choose js-yaml if you need a mature, battle-tested library with deep customization options like custom schemas and tags. It is ideal for complex configuration files or tools where you need strict control over how types are resolved. However, be aware that it defaults to unsafe loading behaviors if not configured correctly, so you must explicitly use the safe loader in production.
Choose yaml if you prioritize spec compliance, security by default, and modern ESM support. It is the best choice for new frontend projects, especially those using Vite, Next.js, or other modern bundlers. Its API is cleaner, it prevents prototype pollution out of the box, and it handles circular references and comments better than older libraries.
Choose yaml-front-matter specifically when building static site generators, documentation tools, or content management systems that need to parse Markdown files with YAML headers. It simplifies the extraction of metadata and content separation, saving you from writing regex or manual string splitting logic for this common pattern.
Avoid yamljs for any new project. It is effectively unmaintained, lacks modern ESM support, and does not receive security updates. Its API feels dated compared to modern alternatives, and it may introduce vulnerabilities or compatibility issues in current build pipelines. Migrate existing usages to yaml or js-yaml.
YAML 1.2 parser and serializer for JavaScript.
npm install js-yaml
import { load } from 'js-yaml'
try {
const document = load('greeting: hello')
console.log(document.greeting)
} catch (e) {
console.error(e)
}
import { dump } from 'js-yaml'
const source = dump({ greeting: 'hello' })
console.log(source)