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

admin-lib-cms

v1.1.7

Published

Agnostic Admin CMS Library

Readme

admin-lib

A lightweight, portable CMS library built for React/Next.js applications, offering seamless database integration, in-page editing, and subscription management.

What is it?

admin-lib provides a set of components and hooks that allow you to easily drop a fully functional CMS interface into your application. It lets your clients or users edit website content directly on the page, manage collections (like blog posts or products), view form submissions, and handle billing/subscriptions, all from a unified admin interface.

How it works

The library works by wrapping your application in a CmsProvider. When users access the admin portal (usually /admin), it loads the AdminApp component which provides the surrounding UI (sidebar, navigation, settings).

The actual preview of your website is loaded in an iframe. Using the Editable component in your application code allows the admin panel to communicate with your site via postMessage. When an admin clicks on an editable element, the sidebar updates to show the correct fields (text, image, rich text) and saves the overrides using the provided StorageAdapter.

Key Features:

  1. In-Page Editing: Use the <Editable /> component to make any text, image, or block of content editable.
  2. Collections: Manage structured data (like articles or team members) through a dynamic table view.
  3. Submissions: Collect and manage form submissions (e.g., contact forms) with built-in email notifications.
  4. Storage Adapters: Connect to any backend using a custom adapter, or use LocalStorageAdapter for quick local testing.
  5. Subscriptions: Built-in UI to manage Stripe subscriptions and billing portals.

Bring Your Own Database (MySQL, PostgreSQL, Sanity)

You are not locked into any specific backend! The library uses a standard StorageAdapter interface, which means you can save your content anywhere.

1. Connecting to MySQL or PostgreSQL

You can create a custom adapter that sends data to your own API routes, which then save the content using Prisma, Drizzle, or raw SQL queries:

import { StorageAdapter } from "admin-lib-cms/types";

export const sqlStorageAdapter: StorageAdapter = {
  name: "mysql-postgres-adapter",
  
  // Fetch data from your database (e.g. via an API route)
  getOverrides: async () => {
    const res = await fetch("/api/cms/content");
    return res.json(); 
  },
  
  // Save data to your database
  setOverrides: async (overrides) => {
    await fetch("/api/cms/content", {
      method: "POST",
      body: JSON.stringify(overrides)
    });
  },

  // Save form submissions
  saveSubmission: async (sub) => {
    await fetch("/api/cms/submissions", {
      method: "POST",
      body: JSON.stringify(sub)
    });
  }
};

2. Connecting to Sanity.io

If you prefer using Sanity as your backend, admin-lib provides a built-in adapter. Just import it and pass your Sanity client:

import { createSanityAdapter } from "admin-lib-cms/storage";
import { createClient } from "@sanity/client";

const sanityClient = createClient({
  projectId: "your-project-id",
  dataset: "production",
  apiVersion: "2024-01-01",
  token: process.env.SANITY_API_TOKEN,
  useCdn: false
});

export const mySanityAdapter = createSanityAdapter({ 
  client: sanityClient 
});

Pass whichever adapter you choose into the <CmsProvider adapter={myAdapter}>.

Installation and Usage

  1. Install the library in your main app:

    npm install admin-lib-cms

    (If you are developing locally, you can use a workspace reference or file path).

  2. Tailwind CSS Configuration (Important): If your project uses Tailwind CSS v4, you must tell the compiler to scan admin-lib-cms for styling classes. Open your main CSS file (e.g., app/globals.css) and add the @source directive right after the tailwind import:

    @import "tailwindcss";
    @source "../node_modules/admin-lib-cms";

3. Define your CMS Configuration

Create a configuration file (e.g. cms.config.ts) to tell the admin panel about your site's structure, the pages that can be edited, and the data collections you want to manage.

import { CmsConfig } from "admin-lib-cms/types";

export const cmsConfig: CmsConfig = {
  siteName: "My Website",
  pages: [
    { id: "home", name: "Home Page", path: "/" },
    { id: "about", name: "About Us", path: "/about" },
  ],
  collections: [
    {
      id: "blog_posts",
      name: "Blog Posts",
      description: "Manage your blog articles.",
      fields: [
        { id: "title", label: "Title", type: "text" },
        { id: "content", label: "Content", type: "richtext" },
        { id: "coverImage", label: "Cover Image", type: "image" },
      ],
    },
  ],
  globals: [
    {
      id: "brand",
      label: "Site Branding",
      fields: [
        { id: "global-logo", label: "Logo", type: "image", defaultValue: "" },
        { id: "global-phone", label: "Phone Number", type: "text", defaultValue: "(555) 123-4567" },
      ],
    },
  ],
};

4. Wrap your application with the CmsProvider

Typically in your layout.tsx or _app.tsx, pass your adapter and configuration to the provider:

import { CmsProvider } from "admin-lib-cms";
import { createLocalStorageAdapter } from "admin-lib-cms/storage";
import { cmsConfig } from "./cms.config";

const adapter = createLocalStorageAdapter();

export default function Layout({ children }) {
  return (
    <CmsProvider config={cmsConfig} adapter={adapter}>
      {children}
    </CmsProvider>
  );
}

5. Subscription & Billing Integration (Optional)

admin-lib-cms includes an integrated subscription portal to manage your users' active states. To enable it, you need to provide the apiUrl and projectId props to your CmsProvider and AdminApp components.

In your CmsProvider Wrapper:

<CmsProvider 
  config={cmsConfig} 
  adapter={adapter}
  apiUrl={process.env.NEXT_PUBLIC_API_URL}
  projectId={process.env.NEXT_PUBLIC_STRIPE_PROJECT_ID}
  // Optional: If you use a custom backend, specify the exact endpoint to validate the subscription
  validateSubscriptionUrl="https://your-custom-backend.com/api/validate"
>
  {children}
</CmsProvider>

If validateSubscriptionUrl is omitted, the library will automatically use ${apiUrl}/api/validate-subscription.

Expected API Request & Response: When validating the subscription, admin-lib-cms will make a POST request to your endpoint with the following body:

{ "projectId": "your-project-id" }

Your API route must return a JSON object with the following structure to properly update the CMS state:

{
  "valid": true,
  "status": "active",
  "expiryDate": "2024-12-31T23:59:59.000Z",
  "_id": "sub_12345",
  "stripeCustomerId": "cus_67890",
  "stripePaymentLink": "https://buy.stripe.com/test_..."
}

(Note: _id, stripeCustomerId, and stripePaymentLink are optional but required if you want to use the "Manage Billing" and "Pay Now" buttons).

  1. Setup Authentication Routes: admin-lib-cms provides a fully featured authentication system (registration, login, and JWT cookies) that works directly with your Storage Adapter. Create an API route at app/api/admin/auth/route.ts:
import { createAuthHandler } from "admin-lib-cms/auth";
import { cmsAdapter } from "@/admin-cms.config"; // Your configured adapter

const handler = createAuthHandler({
  adapter: cmsAdapter,
  jwtSecret: process.env.JWT_SECRET // Your secure secret string
});

export const GET = handler;
export const POST = handler;
  1. Setup the Admin Pages (Dashboard & Login): In your app's routing, create the following pages to render the CMS interface.

Dashboard (app/admin/page.tsx):

import { AdminApp } from "admin-lib-cms";

export default function AdminPage() {
  return <AdminApp 
    apiUrl={process.env.NEXT_PUBLIC_API_URL}
    projectId={process.env.NEXT_PUBLIC_STRIPE_PROJECT_ID}
  />;
}

Login (app/admin/login/page.tsx):

"use client";
import { AdminLogin } from "admin-lib-cms";

export default function LoginPage() {
  return <AdminLogin authApiRoute="/api/admin/auth" redirectRoute="/admin" />;
}
  1. Make elements editable on your pages:

You can use specific Editable components depending on the type of content you want to manage.

Available Editable Components:

| Component | Description | Example Usage | | --- | --- | --- | | EditableText | For plain text or simple rich text elements. | <EditableText id="title">My Title</EditableText> | | EditableImage | For images (automatically renders an <img> tag). | <EditableImage id="logo" src="/logo.png" /> | | EditableLink | For hyperlinks (automatically renders an <a> tag). | <EditableLink id="btn" href="/contact">Contact</EditableLink> | | EditableList | For an array of items (features, team members, etc). | <EditableList id="team" initialItems={team} renderItem={...} /> |

Example Usage in a Page:

import { EditableText, EditableImage, EditableLink, EditableList } from "admin-lib-cms";

export default function HomePage() {
  const features = [{ title: "Fast", desc: "Very fast" }];

  return (
    <main>
      {/* Text element */}
      <EditableText id="hero-title" as="h1" label="Hero Title">
        Welcome to my site
      </EditableText>

      {/* Image element */}
      <EditableImage id="hero-image" label="Hero Image" src="/default-hero.jpg" alt="Hero" />

      {/* Link element */}
      <EditableLink id="hero-cta" href="/about" label="Call to Action">
        Learn More
      </EditableLink>

      {/* List element */}
      <EditableList 
        id="features-list" 
        label="Features"
        initialItems={features}
        renderItem={(item, idx) => (
          <div key={idx}>
            <h3>{item.title}</h3>
            <p>{item.desc}</p>
          </div>
        )}
      />
    </main>
  );
}

Development

To build the library:

npm run build

To run in watch mode during development:

npm run dev

Note on Languages

The entire library and its UI are currently maintained in English to ensure broad compatibility and standard development practices. All previous Spanish texts have been translated.