Use GitHub as a headless CMS. Fetch and transform content stored in a GitHub repository via the GitHub API.
structcms treats a GitHub repository as your content store — no extra infrastructure, no vendor lock-in.
| Feature | structcms | Outstatic | Decap CMS | Contentlayer |
|---|---|---|---|---|
| Zero added infrastructure | ✅ No DB, no server | ✅ | ✅ | |
| Separate content & app repos | ✅ Any repo, any owner | ❌ Same repo only | ❌ Local files only | |
| Private repository support | ✅ Token-based access | ❌ Public repos only | ✅ | |
| Setup requirement | GitHub Token only | GitHub OAuth App | Netlify / OAuth | Local file system |
Store content under a contents/ directory in any GitHub repository. Each piece of content lives in its own slug-named folder with a Markdown file and a JSON metadata file.
your-content-repo/
└── contents/
├── hello-world/
│ ├── content.md
│ └── meta.json
└── my-second-post/
├── content.md
└── meta.json
npm install @asahi-fj/structcmsimport StructCms from "@asahi-fj/structcms";
const cms = StructCms({
token: process.env.GITHUB_TOKEN!,
owner: "your-org",
repo: "your-content-repo",
});
const posts = await cms.getAll();
// [{ slug: "hello-world", content: "# Hello\n...", meta: { title: "Hello World", ... } }]See USAGE.md for full examples.
meta.json
{
"title": "Post title",
"description": "A short description",
"publishedAt": "2026-04-11",
"draft": false,
"tags": ["typescript", "cms"]
}content.md — standard Markdown.
| Method | Description |
|---|---|
cms.getAll() |
Fetch all content entries |
cms.getById(id) |
Fetch a single entry by slug (coming soon) |
- Node.js 18+
- GitHub Personal Access Token with read access to the content repository
ISC