AdminCP Pages

Create powerful admin interfaces for your plugins with protected pages, custom layouts, and integrated navigation.

Pages & Layouts

AdminCP follows the same patterns like from Layouts and Pages but uses the src/app_admin directory.

Pages

plugins/{plugin_name}/src/app_admin/blog/settings/page.tsx
export default function Page() {
  return (
    <div>
      <h1>Settings</h1>
      <p>Hello from Blog plugin in AdminCP</p>
    </div>
  );
}

Layouts

plugins/{plugin_name}/src/app_admin/blog/settings/layout.tsx
export default function AdminRootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="border-b bg-white p-4">
        <h1 className="text-xl font-semibold">Blog Admin</h1>
      </header>
      {children}
    </div>
  );
}

All pages & layouts in the app_admin directory are automatically protected and require admin authentication.

To create own navigation items in the admin sidebar you can just create nav object inside admin in your plugin config.

Define items in config

As an example let's create a navigation item for blog categories:

plugins/{plugin_name}/src/plugin.tsx
import { buildPlugin } from '@vitnode/core/lib/plugin';
import { SettingsIcon } from 'lucide-react';

import { configPlugin } from './config';

export const blogPlugin = () => {
  return buildPlugin({
    ...configPlugin,
    admin: {
      nav: [
        {
          id: 'settings',
          href: '/admin/blog/settings',
          icon: <SettingsIcon />,
        },
      ],
    },
  });
};

Nested Navigation

To create nested navigation items, you can use the items property:

plugins/{plugin_name}/src/plugin.tsx
import { buildPlugin } from '@vitnode/core/lib/plugin';
import { ListIcon, SettingsIcon } from 'lucide-react';
import { configPlugin } from './config';

export const blogPlugin = () => {
  return buildPlugin({
    ...configPlugin,
    admin: {
      nav: [
        {
          id: 'settings',
          href: '/admin/blog/settings',
          icon: <SettingsIcon />,
          items: [
            {
              id: 'general',
              href: '/admin/blog/settings/general',
            },
            {
              id: 'seo',
              href: '/admin/blog/settings/seo',
            },
          ],
        },
      ],
    },
  });
};

Open in new tab

To open a navigation item in a new tab, you can use the isOpenInNewTab property:

plugins/{plugin_name}/src/plugin.tsx
import { buildPlugin } from '@vitnode/core/lib/plugin';
import { SettingsIcon } from 'lucide-react';
import { configPlugin } from './config';
export const blogPlugin = () => {
  return buildPlugin({
    ...configPlugin,
    admin: {
      nav: [
        {
          id: 'settings',
          href: '/admin/blog/settings',
          icon: <SettingsIcon />,
          isOpenInNewTab: true, 
        },
      ],
    },
  });
};

Add translation

VitNode automatically uses the id as translation key for the navigation item.

plugins/{plugin_name}/src/locales/en.json
{
  "@vitnode/blog": {
    "title": "Blog",
    "admin": {
      "nav": {
        "settings": "Settings"
      }
    }
  }
}

Learn More