PayloadCMSExtensions
Plugins

Enhanced Sidebar Plugin

An enhanced sidebar plugin for Payload CMS that adds a tabbed navigation system to organize collections and globals into logical groups.

Development Status

This plugin is in early development and has not been extensively tested. Use with caution in production environments.

Overview

The Payload Enhanced Sidebar Plugin transforms your admin navigation with a modern tabbed interface, helping you organize collections and globals into logical groups for cleaner, more efficient navigation.

Features

  • Tabbed Navigation: Organize collections into separate tabs for cleaner navigation
  • Vertical Tab Bar: Icon-based tabs on the left side of the sidebar
  • Link Support: Add navigation links (like Dashboard) alongside tabs
  • Custom Items: Add custom navigation items that can be merged into existing groups
  • Badges: Show notification badges on tabs and navigation items (API-based or reactive provider)
  • i18n Support: Full localization support for labels and groups
  • Lucide Icons: Use any Lucide icon for tabs and links

Payload Enhanced Sidebar Showcase

Installation

npm install @veiag/payload-enhanced-sidebar

Quick Start

import { payloadEnhancedSidebar } from '@veiag/payload-enhanced-sidebar'
import { buildConfig } from 'payload'

export default buildConfig({
  // ... your config
  plugins: [
    payloadEnhancedSidebar({
      // Works with defaults!
    }),
  ],
})

This will add:

  • A Dashboard link at the top
  • A default tab showing all collections and globals
  • A logout button at the bottom

Default Configuration

Configuration

Full Configuration Example

import { payloadEnhancedSidebar } from '@veiag/payload-enhanced-sidebar'
import { buildConfig } from 'payload'

export default buildConfig({
  plugins: [
    payloadEnhancedSidebar({
      // Tabs and links in the sidebar
      tabs: [
        // Dashboard link
        {
          id: 'dashboard',
          type: 'link',
          href: '/',
          icon: 'House',
          label: { en: 'Dashboard', uk: 'Головна' },
        },
        // Content tab - shows specific collections
        {
          id: 'content',
          type: 'tab',
          icon: 'FileText',
          label: { en: 'Content', uk: 'Контент' },
          collections: ['posts', 'pages', 'categories'],
        },
        // Link to external documentation
        {
          id: 'docs',
          type: 'link',
          href: 'https://payloadcms.com/',
          icon: 'BookOpen',
          isExternal: true,
          label: { en: 'Documentation', uk: 'Документація' },
        },
        // E-commerce tab with custom items
        {
          id: 'ecommerce',
          type: 'tab',
          icon: 'ShoppingCart',
          label: { en: 'E-commerce', uk: 'E-commerce' },
          collections: ['products', 'orders', 'customers'],
          customItems: [
            {
              slug: 'analytics',
              href: '/analytics',
              label: { en: 'Analytics', uk: 'Аналітика' },
              group: 'E-commerce', // Merge into existing group
            },
          ],
        },
        // Settings tab with globals
        {
          id: 'settings',
          type: 'tab',
          icon: 'Settings',
          label: { en: 'Settings', uk: 'Налаштування' },
          collections: ['users'],
          globals: ['site-settings', 'footer-settings'],
          customItems: [
            {
              slug: 'api-keys',
              href: '/api-keys',
              label: { en: 'API Keys', uk: 'API Ключі' },
              // No group - will appear at the bottom
            },
            {
              slug:'external-link',
              href: 'https://example.com',
              isExternal: true,
              label: { en: 'External Link', uk: 'Зовнішнє Посилання'}
            }
          ],
        },
      ],

      // Show/hide logout button (default: true)
      showLogout: true,

      // Disable the plugin
      disabled: false,
    }),
  ],
})

Plugin Options

tabs

Array of tabs and links to show in the sidebar.

Tab Configuration (type: 'tab')

Prop

Type

Default Behavior

If neither collections nor globals are specified, the tab shows all collections and globals.

Prop

Type

Tab vs Link Active States

customItems

Custom items can be added to any tab to extend navigation functionality:

{
  slug: 'unique-slug',           // Required: unique identifier
  href: '/path',                 // Required: URL (relative or absolute)
  label: { en: 'Label' },        // Required: display label
  group: { en: 'Group Name' },   // Optional: merge into existing group or create new
  isExternal: true,              // Optional: if true, href is absolute URL
}

Group Behavior

  • Existing Group: If group matches an existing collection group label, the item is added to that group
  • New Group: If group doesn't match any existing group, a new group is created
  • Ungrouped: If group is not specified, the item appears at the bottom without grouping

General Options

Prop

Type

Badges

Badges allow you to show notification counts on tabs and navigation items, perfect for drawing attention to pending tasks, unread notifications, or item counts.

Badge Configuration

There are three ways to configure badges:

Add a badge property directly to any tab or link:

tabs: [
  {
    id: 'orders',
    type: 'tab',
    icon: 'ShoppingCart',
    label: 'Orders',
    collections: ['orders'],
    badge: {
      type: 'collection-count',
      collectionSlug: 'orders',
      color: 'error',
    },
  },
]

2. Badges on Navigation Items

Use the badges configuration to add badges to sidebar items:

payloadEnhancedSidebar({
  badges: {
    // Show document count for posts collection
    posts: { type: 'collection-count', color: 'primary' },
    // Custom API endpoint
    orders: {
      type: 'api',
      endpoint: '/api/orders/pending',
      responseKey: 'count',
      color: 'error',
    },
    // Provider-based (reactive)
    notifications: { type: 'provider', color: 'warning' },
  },
})

Badge Types

Collection Count

Automatically fetches document count from a collection:

Prop

Type

{
  type: 'collection-count',
  collectionSlug: 'orders',
  color: 'error',
  where: { status: { equals: 'pending' } },
}

API Endpoint

Fetches badge value from a custom API endpoint:

Prop

Type

{
  type: 'api',
  endpoint: '/api/orders/pending',
  method: 'GET',
  responseKey: 'count',
  color: 'error',
}

Provider (Reactive)

Uses reactive values from BadgeProvider context for real-time updates:

Prop

Type

{
  type: 'provider',
  slug: 'notifications',
  color: 'warning',
}

Using BadgeProvider

For reactive badges with real-time updates (websockets, polling, etc.):

Step 1: Create a provider component:

// components/MyBadgeProvider.tsx
'use client'

import { BadgeProvider } from '@veiag/payload-enhanced-sidebar'
import { useEffect, useState } from 'react'

export const MyBadgeProvider = ({ children }) => {
  const [counts, setCounts] = useState({
    orders: 0,
    notifications: 0,
  })

  useEffect(() => {
    // Fetch initial counts, subscribe to websocket, etc.
    const ws = new WebSocket('wss://your-api/counts')
    ws.onmessage = (e) => setCounts(JSON.parse(e.data))
    return () => ws.close()
  }, [])

  return <BadgeProvider values={counts}>{children}</BadgeProvider>
}

Step 2: Add it to Payload's providers:

// payload.config.ts
export default buildConfig({
  admin: {
    components: {
      providers: ['./components/MyBadgeProvider#MyBadgeProvider'],
    },
  },
})

Step 3: Configure badges to use the provider:

payloadEnhancedSidebar({
  badges: {
    orders: { type: 'provider', color: 'error' },
  },
  tabs: [
    {
      id: 'notifications',
      type: 'link',
      href: '/notifications',
      icon: 'Bell',
      label: 'Notifications',
      badge: { type: 'provider', slug: 'notifications', color: 'warning' },
    },
  ],
})

Badge Colors

Available color themes: default, primary, success, warning, error

Badge Color Options

Badge Display Rules

  • Numbers up to 99 are shown as-is
  • Numbers > 99 are shown as "99+"
  • Zero or undefined values hide the badge
  • Provider values can also be React nodes for custom rendering

Localization

All labels support localized strings for internationalization:

// Simple string
label: 'Dashboard'

// Localized object
label: {
  en: 'Dashboard',
  uk: 'Головна',
  de: 'Übersicht',
  fr: 'Tableau de bord',
}

The plugin will automatically use the appropriate translation based on the user's locale settings.

Payload Features Support

The plugin seamlessly integrates with Payload's built-in features:

  • Browse by Folder Button: Automatically shows folder view button when Payload folders are enabled (requires Payload v3.41.0+)
  • Settings Menu Items: Integrates with Payload's SettingsMenu components (requires Payload v3.60.0+)

Examples

Blog Setup

payloadEnhancedSidebar({
  tabs: [
    {
      id: 'dashboard',
      type: 'link',
      href: '/',
      icon: 'House',
      label: 'Dashboard',
    },
    {
      id: 'content',
      type: 'tab',
      icon: 'FileText',
      label: 'Content',
      collections: ['posts', 'pages', 'categories'],
      badge: {
        type: 'collection-count',
        collectionSlug: 'posts',
        where: { status: { equals: 'draft' } },
        color: 'warning',
      },
    },
    {
      id: 'users',
      type: 'tab',
      icon: 'Users',
      label: 'Users',
      collections: ['users'],
      globals: ['site-settings'],
    },
  ],
})

E-commerce Setup

payloadEnhancedSidebar({
  tabs: [
    {
      id: 'dashboard',
      type: 'link',
      href: '/',
      icon: 'BarChart',
      label: 'Dashboard',
    },
    {
      id: 'catalog',
      type: 'tab',
      icon: 'Package',
      label: 'Catalog',
      collections: ['products', 'categories', 'brands'],
    },
    {
      id: 'orders',
      type: 'tab',
      icon: 'ShoppingCart',
      label: 'Orders',
      collections: ['orders', 'customers'],
      badge: {
        type: 'api',
        endpoint: '/api/orders/pending',
        color: 'error',
      },
    },
    {
      id: 'settings',
      type: 'tab',
      icon: 'Settings',
      label: 'Settings',
      collections: ['users'],
      globals: ['shipping-settings', 'payment-settings'],
    },
  ],
})

Multi-language Platform

payloadEnhancedSidebar({
  tabs: [
    {
      id: 'dashboard',
      type: 'link',
      href: '/',
      icon: 'Home',
      label: {
        en: 'Dashboard',
        uk: 'Головна',
        de: 'Übersicht',
      },
    },
    {
      id: 'content',
      type: 'tab',
      icon: 'FileText',
      label: {
        en: 'Content',
        uk: 'Контент',
        de: 'Inhalt',
      },
      collections: ['articles', 'pages'],
    },
    {
      id: 'docs',
      type: 'link',
      href: 'https://docs.example.com',
      icon: 'BookOpen',
      isExternal: true,
      label: {
        en: 'Documentation',
        uk: 'Документація',
        de: 'Dokumentation',
      },
    },
  ],
})

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Issues

Found a bug or have a feature request? Please open an issue on GitHub.