Building a Markdown Content Loader with Vite import.meta.glob
This site's blog and tech notes don't use a Node-only frontmatter parser like gray-matter. Libraries like that tend to reference Node's Buffer internally, and if that ends up in the browser bundle it usually surfaces as a Buffer is not defined error during hydration. Instead, Vite's import.meta.glob collects the markdown files as raw strings at build time, and a handful of regexes act as a minimal parser that pulls out just the frontmatter.
const rawFilesByLocale = {
ko: import.meta.glob<string>('/markdown/ko/*.md', {
query: '?raw',
import: 'default',
eager: true,
}),
en: import.meta.glob<string>('/markdown/en/*.md', {
query: '?raw',
import: 'default',
eager: true,
}),
}
Passing eager: true fills the object with every file's contents at build time, with no dynamic import step needed — which is exactly what a statically-built site needs, since there's no file system to read from at runtime.
A content loader can only be reused once it stops knowing what kind of content it's loading.
Hardcoding this loader for the blog alone means every new content type (tech notes, in this case) ends up duplicating the same parsing and merging logic. In practice, the loader was split into a createContentSource() factory that only takes a glob result and a structured-article array as arguments, and the blog and tech-notes sources each call it once with their own markdown folder path. Because import.meta.glob's pattern has to be a static string literal at its call site, the glob calls themselves stay in each content-source file, and only their results get handed to the shared factory.