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

@vijay_j/next-admin

v1.0.1

Published

Plug-and-Play Admin UI for Modern Next.js Backends

Readme

@vijay_j/next-admin

Plug-and-Play Admin UI for Modern Next.js Backends

A drop-in admin panel optimized for Next.js App Router with minimal configuration and opinionated defaults.

npm version License: MIT

Features

  • 🚀 Zero-config first - Works out of the box with sensible defaults
  • 📦 Prisma-first - First-class Prisma integration with schema introspection
  • 🔌 REST API support - Flexible adapter for any REST backend
  • 🔐 Built-in auth - NextAuth, Clerk, and Supabase support
  • 🛡️ Permissions - Role-based access control at model and field level
  • 📊 Smart CRUD - Auto-generated list, detail, create, and edit views
  • 🎨 Beautiful UI - Clean, responsive design with dark mode
  • Type-safe - Full TypeScript support

Installation

npm install @vijay_j/next-admin
# or
yarn add @vijay_j/next-admin
# or
pnpm add @vijay_j/next-admin

Quick Start

Initialize (Optional CLI)

npx @vijay_j/next-admin init

This will:

  1. Detect your Next.js App Router
  2. Detect your Prisma schema (if present)
  3. Create /admin route with layout and auth guard
  4. Auto-register your models
  5. Generate CRUD pages for each model

Visit Your Admin

npm run dev
# Open http://localhost:3000/admin

Configuration

Basic Setup

// app/admin/admin.config.ts
import { defineAdmin } from '@vijay_j/next-admin';

export const adminConfig = defineAdmin({
  title: 'My Admin Panel',
  basePath: '/admin',
  auth: {
    provider: 'nextauth', // or 'clerk', 'supabase'
    loginUrl: '/login',
  },
  models: {
    User: {
      label: 'User',
      labelPlural: 'Users',
      icon: 'Users',
      fields: {
        id: { hidden: true },
        email: { type: 'email', searchable: true },
        name: { searchable: true },
        role: { type: 'enum', filterable: true, options: [
          { label: 'Admin', value: 'admin' },
          { label: 'User', value: 'user' },
        ]},
        createdAt: { type: 'datetime', readonly: true },
      },
      permissions: {
        read: ['admin', 'support'],
        create: ['admin'],
        update: ['admin'],
        delete: ['admin'],
      },
    },
  },
});

Field Types

| Type | Description | |------|-------------| | string | Text input | | number | Number input | | boolean | Toggle/checkbox | | datetime | Date and time picker | | date | Date picker | | email | Email input with validation | | password | Password input with toggle | | url | URL input | | textarea | Multi-line text | | richtext | Rich text editor | | json | JSON editor | | enum | Select dropdown | | relation | Related model picker | | file | File upload | | image | Image upload with preview |

Field Configuration

fields: {
  name: {
    label: 'Full Name',           // Display label
    description: 'Enter name',    // Help text
    placeholder: 'John Doe',      // Input placeholder
    required: true,               // Validation
    readonly: false,              // Can edit?
    hidden: false,                // Hide everywhere
    hiddenInList: false,          // Hide in table
    hiddenInDetail: false,        // Hide in detail view
    hiddenInForm: false,          // Hide in forms
    searchable: true,             // Include in search
    filterable: true,             // Show filter option
    sortable: true,               // Allow sorting
    width: 200,                   // Column width
    
    // For enum type
    options: [
      { label: 'Active', value: 'active' },
      { label: 'Inactive', value: 'inactive' },
    ],
    
    // For relation type
    relation: {
      model: 'Organization',
      displayField: 'name',
      valueField: 'id',
    },
    
    // Custom rendering
    renderList: (value, record) => <Badge>{value}</Badge>,
    renderDetail: (value, record) => <div>{value}</div>,
    
    // Custom validation
    validation: z.string().min(2).max(100),
  },
}

Authentication

NextAuth

// app/admin/layout.tsx
import { getServerSession } from 'next-auth';
import { redirect } from 'next/navigation';
import { AdminLayout } from 'next-admin/client';
import { adminConfig } from './admin.config';
import { authOptions } from '@/lib/auth';

export default async function Layout({ children }) {
  const session = await getServerSession(authOptions);
  
  if (!session) {
    redirect('/api/auth/signin');
  }
  
  return (
    <AdminLayout config={adminConfig} user={session.user}>
      {children}
    </AdminLayout>
  );
}

Clerk

import { auth, currentUser } from '@clerk/nextjs';

export default async function Layout({ children }) {
  const { userId } = auth();
  
  if (!userId) {
    redirect('/sign-in');
  }
  
  const user = await currentUser();
  
  return (
    <AdminLayout 
      config={adminConfig} 
      user={{
        id: user.id,
        email: user.emailAddresses[0]?.emailAddress,
        name: `${user.firstName} ${user.lastName}`,
        roles: user.publicMetadata.roles || ['user'],
      }}
    >
      {children}
    </AdminLayout>
  );
}

Permissions

Model-Level Permissions

permissions: {
  read: ['admin', 'support'],    // Who can view
  create: ['admin'],              // Who can create
  update: ['admin', 'support'],   // Who can edit
  delete: ['admin'],              // Who can delete
}

Field-Level Permissions

permissions: {
  read: ['admin'],
  write: ['admin'],
  fields: {
    salary: {
      read: ['admin', 'hr'],
      write: ['admin'],
    },
    ssn: {
      read: ['admin'],
      write: ['admin'],
    },
  },
}

Super Admin

export const adminConfig = defineAdmin({
  permissions: {
    adminRoles: ['admin', 'support'],
    superAdminRoles: ['super_admin'], // Bypasses all permissions
  },
  // ...
});

Custom Actions

actions: [
  {
    id: 'ban-user',
    label: 'Ban User',
    icon: 'Ban',
    variant: 'destructive',
    confirmation: 'Are you sure you want to ban this user?',
    roles: ['admin'],
    handler: async (ids, context) => {
      await banUsers(ids);
      return { success: true, message: 'Users banned' };
    },
  },
  {
    id: 'send-email',
    label: 'Send Email',
    icon: 'Mail',
    bulk: true, // Available for bulk selection
    handler: async (ids) => {
      await sendBulkEmail(ids);
      return { success: true };
    },
  },
],

Data Adapters

Prisma (Default)

import { createPrismaAdapter } from 'next-admin/adapters/prisma';
import { prisma } from '@/lib/prisma';

export const adminConfig = defineAdmin({
  adapter: createPrismaAdapter({
    prisma,
    queryModifiers: {
      User: {
        include: { organization: true },
      },
    },
  }),
  // ...
});

REST API

import { createRestAdapter } from 'next-admin/adapters/rest';

export const adminConfig = defineAdmin({
  adapter: createRestAdapter({
    baseUrl: 'https://api.example.com',
    headers: {
      'Authorization': `Bearer ${token}`,
    },
    endpoints: {
      User: {
        list: '/users',
        get: '/users/{id}',
        create: '/users',
        update: '/users/{id}',
        delete: '/users/{id}',
      },
    },
  }),
  // ...
});

Navigation

navigation: [
  {
    title: 'Dashboard',
    href: '/admin',
    icon: 'LayoutDashboard',
  },
  {
    title: 'Content',
    items: [
      { title: 'Posts', href: '/admin/post', icon: 'FileText' },
      { title: 'Categories', href: '/admin/category', icon: 'Folder' },
    ],
  },
  {
    title: 'Users',
    href: '/admin/user',
    icon: 'Users',
    badge: '5',
    badgeVariant: 'destructive',
    roles: ['admin'], // Only visible to admins
  },
],

Audit Logging

audit: {
  enabled: true,
  models: ['User', 'Order'], // Empty = all models
  handler: async (entry) => {
    // Custom handler - save to database, send to service, etc.
    await prisma.auditLog.create({ data: entry });
  },
},

Add the audit log table to your Prisma schema:

model AdminAuditLog {
  id        String   @id @default(cuid())
  timestamp DateTime @default(now())
  userId    String
  userName  String?
  action    String
  model     String
  recordId  String
  changes   Json?
  metadata  Json?

  @@index([model, recordId])
  @@index([userId])
  @@index([timestamp])
}

Customization

Custom Components

// Override list view for a model
User: {
  listComponent: CustomUserList,
  detailComponent: CustomUserDetail,
  formComponent: CustomUserForm,
}

// Or use custom field renderers
fields: {
  status: {
    renderList: (value) => (
      <Badge variant={value === 'active' ? 'success' : 'secondary'}>
        {value}
      </Badge>
    ),
  },
}

Theme

theme: {
  primaryColor: 'blue',
  darkMode: true,
  defaultColorScheme: 'system',
  logo: '/logo.svg', // or: logo: CustomLogoComponent
  cssVariables: {
    '--primary': '221.2 83.2% 53.3%',
  },
},

Environment Badge

environment: process.env.NODE_ENV === 'production' 
  ? 'production' 
  : 'development',

CLI Commands

# Initialize admin panel
npx @vijay_j/next-admin init

# Generate pages for specific models
npx @vijay_j/next-admin generate --models User,Order

# Generate for all models
npx @vijay_j/next-admin generate --all

# Add a new model
npx @vijay_j/next-admin add-model Product
npx @vijay_j/next-admin add-model Product --rest  # Using REST adapter

Example Application

A complete example application is included in the example/ directory. To run it:

# Navigate to example directory
cd example

# Install dependencies
npm install

# Generate Prisma client and run migrations
npx prisma generate
npx prisma migrate dev

# Seed the database with sample data
npm run seed

# Start the development server
npm run dev

# Open http://localhost:3000

Test Accounts

| Email | Password | Role | |-------|----------|------| | [email protected] | admin123 | ADMIN | | [email protected] | super123 | SUPER_ADMIN | | [email protected] | editor123 | EDITOR | | [email protected] | password123 | USER |

Features Demonstrated

  • Dashboard - Stats overview with recent orders
  • User Management - CRUD with role management
  • Posts - Content management with categories and tags
  • Products - E-commerce product management
  • Orders - Order management with status updates
  • Settings - System configuration
  • Audit Log - Track all changes

Production Checklist

  • [ ] Set up proper authentication
  • [ ] Configure role-based permissions
  • [ ] Enable audit logging
  • [ ] Set environment badge
  • [ ] Configure proper CORS for API routes
  • [ ] Enable rate limiting on admin routes
  • [ ] Set up proper error monitoring
  • [ ] Test all CRUD operations
  • [ ] Review field visibility settings
  • [ ] Test with different user roles

TypeScript Support

Full TypeScript support is included:

import type { AdminConfig, AdminUser, ModelConfig, FieldConfig } from '@vijay_j/next-admin';

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

MIT © vijay_j