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

@createcms/core

v0.3.0

Published

[Work in progress · pre-1.0 · not production-ready] Composable, block-based headless CMS powered by better-call and Drizzle ORM (Postgres). Database-native versioning with Git-like branches, copy-on-write drafts, visual diffs, merges, reusable blocks, nes

Readme

@createcms/core

A composable, block-based headless CMS powered by better-call and Drizzle ORM (PostgreSQL). Database-native, Git-like versioning with branches, copy-on-write drafts, visual diffs, merges, reusable blocks, nested pages, and a fully type-safe API.

⚠️ Work in progress — not production-ready. This package is pre-1.0, under active development, and has not been tested in production. Expect breaking changes, rough edges, and bugs (including possible data-loss edge cases). Use it for prototyping and exploration — not for production workloads. Pin an exact version.

Documentation · Examples · Changelog · Contributing

Features

  • Block-based content — define collections as a typed root plus child blocks; content is a tree of blocks.
  • Git-like versioning — every collection entry is a root with branches, commits, and snapshots. Edit on a branch, open a merge request, diff, resolve conflicts, merge, publish.
  • End-to-end type safety — collection definitions drive both the write API and the read responses. Autocomplete on properties everywhere, no manual types.
  • Type-safe client — a proxy-based client mirrors the server API: client.pages.listRoots() is fully typed from typeof cms.
  • Plugins — extend the server API, client, hooks, schema, and request scope. Ships with multi-tenant, i18n, A/B testing, client-side media optimization, and consent.
  • Framework-friendly — first-class Next.js route mounting and React rendering helpers; the core is framework-agnostic.

Installation

npm install @createcms/core
# required peers
npm install drizzle-orm react

Requirements

  • Node.js ≥ 20 — per the package engines.
  • PostgreSQL — the CMS is database-native (Drizzle + Postgres).
  • Required peersdrizzle-orm@^0.45 and react@>=18.
  • Optionalnext@>=16 for the first-class Next.js integration.

Quick start

0. Scaffold a project (optional)

Bootstrap the CMS config, a sample collection, and the Next.js route handler into an existing project (assumes the src/ layout + the @/* path alias):

npx createcms init               # interactive preset picker
npx createcms init --preset blog # or pick directly: pages | blog | docs

It writes src/lib/cms.ts, a collection under src/cms/collections/, the src/app/api/cms/[[...rest]]/route.ts route handler and a .env.example, and adds a cms:generate script — never overwriting existing files. Pick a starting collection with --preset (pages — marketing/content pages, the default; blog — posts with excerpt, date, cover image; docs — nested docs with callouts); the scaffolded collection is editable code you own. Provide your own Drizzle client at src/lib/db.ts, then continue below.

1. Define collections

import { defineCollection, defineCollections } from '@createcms/core';

const pages = defineCollection({
  label: 'Pages',
  slug: { enabled: true, prefix: '/', nested: true },
  root: {
    properties: {
      title: { type: 'string', required: true, label: 'Title' },
    },
  },
  blocks: {
    hero: {
      label: 'Hero',
      properties: {
        headline: { type: 'string', required: true, label: 'Headline' },
        align: {
          type: 'select',
          label: 'Align',
          options: [
            { label: 'Left', value: 'left' },
            { label: 'Right', value: 'right' },
          ],
        },
      },
    },
  },
});

export const collections = defineCollections({ pages });

2. Create the CMS

import { createCMS } from '@createcms/core';
import { db } from './db';
import { collections } from './collections';

export const cms = createCMS({
  db,
  collections,
  media: { /* S3 / DigitalOcean config */ },
  authMiddleware: async (ctx) => {
    // resolve the user / permissions for this request
    return { userId: '...' };
  },
});

3. Generate the database schema

createcms generate discovers and loads this config (searching cms.ts, src/cms.ts, src/lib/cms.ts), so create it first. It emits a Drizzle schema for your collections + plugins; then run your migrations as usual:

npx createcms generate

4. Mount the HTTP router (Next.js)

// app/api/cms/[[...rest]]/route.ts
import { cms } from '@/lib/cms';

const { handler } = cms.router;
export const GET = handler;
export const POST = handler;

5. Read & render content

Server-side, call the typed API directly. Import the renderer from @createcms/core/react/blocks, which is RSC-safe — the @createcms/core/react barrel is a client boundary ('use client').

import { cms } from '@/lib/cms';
import { BlocksRenderer, createBlocksMap } from '@createcms/core/react/blocks';
import { pagesCollection } from '@/cms/collections/pages';

// Pass the collection DEFINITION — it types the component props and carries
// each block's declared `events` for A/B goal tracking (single source of truth).
const pageBlocks = createBlocksMap(pagesCollection, {
  hero: ({ properties }) => <h1>{properties.headline}</h1>,
});

export default async function Page() {
  const { variants } = await cms.api.pages.getPublishedContent({
    query: { slug: '/' },
  });
  return <BlocksRenderer blocks={pageBlocks} tree={variants[0].tree} />;
}

6. The type-safe client

import { createCMSClient } from '@createcms/core';
import type { cms } from '@/lib/cms';

export const cmsClient = createCMSClient<typeof cms>({ baseURL: '/api/cms' });

const { roots } = await cmsClient.pages.listRoots();
roots[0].properties.title; // typed as `string`

Plugins

import { createCMS } from '@createcms/core';
import { multiTenant } from '@createcms/core/plugins/multi-tenant';
import { abTest } from '@createcms/core/plugins/ab-test';

export const cms = createCMS({
  db,
  collections,
  media,
  plugins: [multiTenant(), abTest({ /* ... */ })],
});

| Plugin | Entry | What it adds | | --- | --- | --- | | Multi-tenant | @createcms/core/plugins/multi-tenant | Per-tenant data isolation via request-scoped query conditions. | | A/B testing | @createcms/core/plugins/ab-test | Deterministic variant assignment, event tracking, pluggable analytics. | | Media optimize | @createcms/core/plugins/media-optimize | Client-side resize/compress/WebP before upload. | | i18n | @createcms/core/plugins/i18n | Per-entry translations across a fixed language universe with per-language fallback chains; adds translation tables and language-scoped read/write endpoints. | | Consent | @createcms/core/plugins/consent | Google Consent Mode v2 gate (buffer-then-flush) with CMP/dataLayer auto-read and a <ConsentGate> wrapper for consent-gated tracking and embeds. |

License

MIT