javascript
45 lines · 9 steps
Parsing YAML-style frontmatter in JavaScript
A small parser that pulls a key-value header block off a Markdown document and coerces each value to its natural type.
Explained by
highlit
1const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
2
3function coerce(value) {
4 const trimmed = value.trim();
5 if (trimmed === '') return '';
6 if (trimmed === 'true') return true;
7 if (trimmed === 'false') return false;
8 if (trimmed === 'null' || trimmed === '~') return null;
9
10 if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
11 (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
12 return trimmed.slice(1, -1);
13 }
14
15 if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
16 const inner = trimmed.slice(1, -1).trim();
17 if (inner === '') return [];
18 return inner.split(',').map((item) => coerce(item));
19 }
20
21 if (/^-?\d+$/.test(trimmed)) return parseInt(trimmed, 10);
22 if (/^-?\d*\.\d+$/.test(trimmed)) return parseFloat(trimmed);
23
24 return trimmed;
25}
26
27export function parseFrontmatter(markdown) {
28 const match = markdown.match(FRONTMATTER_RE);
29 if (!match) {
30 return { data: {}, content: markdown };
31 }
32
33 const data = {};
34 for (const line of match[1].split(/\r?\n/)) {
35 if (!line.trim() || line.trimStart().startsWith('#')) continue;
36
37 const sep = line.indexOf(':');
38 if (sep === -1) continue;
39
40 const key = line.slice(0, sep).trim();
41 if (key) data[key] = coerce(line.slice(sep + 1));
42 }
43
44 return { data, content: markdown.slice(match[0].length) };
45}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A single anchored regex can both detect and slice off a structured header in one pass.
- 2Coercion functions turn raw strings into typed values by checking cheap, specific cases first.
- 3Recursing a coercion routine over split parts lets scalar logic handle nested structures like arrays for free.
Related explainers
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 steps
javascript
import { useEffect, useRef, useState } from 'react'; export function useDelayedFlag(active, delay = 300) { const [visible, setVisible] = useState(false);
Delaying a loading spinner with a React hook
custom-hooks
debouncing
cleanup
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-yaml-style-frontmatter-in-javascript-explained-javascript-749b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.