npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

next-posts

v1.0.0

Published

Load the article and parse the YAML into Next.js!

Readme

next-posts

Load posts and parse YAML into Next.js!

A lightweight, zero-dependency TypeScript library for building static blog posts and content pages in Next.js. It parses Markdown files with YAML frontmatter and provides simple utility functions designed for use with SSG (Static Site Generation).

中文


Features

  • Zero runtime dependencies — only uses built-in Node.js modules
  • Full TypeScript support (generic metadata support)
  • Supports arbitrary directory structures
  • Accepts both slug and slug.md formats
  • Compatible with Next.js App Router and Pages Router

Installation

Install using npm or your preferred package manager:

npm install next-posts

Quick Start

Create a posts/ directory in your project root and add Markdown files:

my-next-app/
└── posts/
    ├── hello-world.md
    └── getting-started.md

Each file should contain a YAML frontmatter block and content:

---
title: Hello World
date: 2024-01-01
tags:
  - blog
  - intro
---

Welcome to my blog!

Then use it in your Next.js project:

import { getAllPosts, getPostBySlug, getAllPostParams } from "next-posts";

interface PostMeta {
  title: string;
  date: string;
  tags?: string[];
}

// Get all posts
const posts = getAllPosts<PostMeta>();

// Get a single post
const post = getPostBySlug<PostMeta>("hello-world");

// Generate static paths (Next.js)
const paths = getAllPostParams();

Template

You can also start with a template:


API

getAllPosts<T>(directory?)

Returns all posts from the specified directory, including parsed frontmatter and content.

const posts = getAllPosts<PostMeta>();
// [
//   { slug: 'hello-world', metadata: { title: '...' }, content: '...' },
//   ...
// ]

| Parameter | Type | Default | Description | | ----------- | -------- | ---------- | -------------------------------- | | directory | string | "posts/" | Path relative to process.cwd() |

Return type: Array<{ slug: string; metadata: T; content: string }>


getPostBySlug<T>(slug, directory?)

Returns a single post by slug. Supports both with and without the .md extension.

const post = getPostBySlug<PostMeta>("hello-world");
// {
//   slug: 'hello-world',
//   metadata: { title: 'Hello World', ... },
//   content: '...'
// }

| Parameter | Type | Default | Description | | ----------- | -------- | ---------- | -------------------------------- | | slug | string | — | Filename (with or without .md) | | directory | string | "posts/" | Path relative to process.cwd() |

Return type: { slug: string; metadata: T; content: string }


getAllPostSlugs(directory?)

Returns all raw filenames (including the .md extension) from the specified directory.

const slugs = getAllPostSlugs();
// ['hello-world.md', 'getting-started.md']

| Parameter | Type | Default | Description | | ----------- | -------- | ---------- | -------------------------------- | | directory | string | "posts/" | Path relative to process.cwd() |

Return type: string[]


getAllPostParams(directory?)

Returns slug parameter objects suitable for Next.js generateStaticParams or getStaticPaths. Automatically removes the .md extension.

const params = getAllPostParams();
// [{ slug: 'hello-world' }, { slug: 'getting-started' }]

| Parameter | Type | Default | Description | | ----------- | -------- | ---------- | -------------------------------- | | directory | string | "posts/" | Path relative to process.cwd() |

Return type: Array<{ slug: string }>


parseFrontmatter(input)

Parses a raw Markdown string and extracts YAML frontmatter. Useful when Markdown is not loaded from the filesystem or when lower-level control is needed.

import { parseFrontmatter } from "next-posts";

const raw = `---
title: Hello World
date: 2024-01-01
tags:
  - blog
---

Welcome to my blog!`;

const { data, content } = parseFrontmatter(raw);
  • data: Parsed YAML frontmatter object. Returns {} if no valid frontmatter exists.
  • content: Markdown content with the frontmatter removed.

| Parameter | Type | Description | | --------- | -------- | ------------------------------------------ | | input | string | Raw Markdown string containing frontmatter |

Return type: { data: Record<string, unknown>; content: string }


Using with Next.js

App Router

// app/blog/[slug]/page.tsx
import { getAllPostParams, getPostBySlug } from "next-posts";

interface PostMeta {
  title: string;
  date: string;
}

export function generateStaticParams() {
  return getAllPostParams();
}

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const { metadata, content } = getPostBySlug<PostMeta>(slug);

  return (
    <article>
      <h1>{metadata.title}</h1>
      <p>{metadata.date}</p>
      {/* ReactMarkdown is recommended, but you can use any renderer you prefer */}
      <ReactMarkdown>{content}</ReactMarkdown>
    </article>
  );
}

Pages Router

// pages/blog/[slug].tsx
import { GetStaticPaths, GetStaticProps } from "next";
import { getAllPostParams, getPostBySlug } from "next-posts";

interface PostMeta {
  title: string;
  date: string;
}

export const getStaticPaths: GetStaticPaths = () => ({
  paths: getAllPostParams().map(({ slug }) => ({ params: { slug } })),
  fallback: false,
});

export const getStaticProps: GetStaticProps = ({ params }) => {
  const post = getPostBySlug<PostMeta>(params!.slug as string);
  return { props: { post } };
};

Custom Post Directory

All functions accept a custom directory:

const posts = getAllPosts("content/articles/");
const post = getPostBySlug("my-article", "content/articles/");

Migrating from v0.x

Migration is straightforward since most APIs remain compatible.

  1. Package rename:

next-posts was previously called next-staticblog. Simply replace the package name and install the latest version. The old package is deprecated and no longer maintained.

  1. Explicit typing:

In v0.x, metadata was typed as any. In v1.x, it is typed as unknown to improve type safety. Refer to the documentation on how to pass generics for safe typing.

Additionally, the package removed gray-matter and now uses @std/yaml for parsing. While all tests pass, there may be minor differences in some non-standard use cases.


FAQ

no such file or directory

SSG is strongly recommended for maximum efficiency. Using next-posts inside dynamic routes may cause errors.

Typically, you only need to combine getAllPostParams() with generateStaticParams() to enable SSG.

Error: ENOENT: no such file or directory, scandir '/var/task/posts/news/zh'

If you insist on using dynamic routes, add the following configuration to next.config.ts to ensure Markdown files are included:

// next.config.ts
outputFileTracingIncludes: {
  "/**": ["./posts/**/*.{md,mdx}"],
},

Contributing

You can improve this package by reporting issues or submitting new features!

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Format code
npm run format

License

MIT © yd-tw https://github.com/yd-tw/next-posts