PluginsAdmin

AdminCP Pages

Ship custom plugin pages inside the VitNode admin panel with sidebar navigation, breadcrumbs, and staff permission gates.

An AdminCP page is an ordinary plugin route configured with area: "admin". VitNode frames your page with the admin panel's sidebar, breadcrumbs header, command palette, and authenticated staff session.

Add an AdminCP Page

1. Declare Route in Manifest

Add the route to src/routes/manifest.ts with area: "admin":

plugins/blog/src/routes/manifest.ts
import type { PluginRouteDefinition } from "@vitnode/core/routing"

export const routes: PluginRouteDefinition[] = [
  {
    area: "admin",
    entry: "routes/admin-settings",
    id: "settings",
    namespaces: ["@vitnode/blog.admin.settings"],
    path: "/admin/blog/settings",
  },
]

2. Create the Page Component

Export a React component as default:

plugins/blog/src/routes/admin-settings.tsx
import { useTranslations } from "use-intl"

const AdminSettingsPage = () => {
  const t = useTranslations("@vitnode/blog.admin.settings")

  return (
    <div className="flex flex-col gap-4 p-4">
      <header className="flex flex-col gap-2">
        <h1 className="text-2xl font-bold tracking-tight">{t("title")}</h1>
        <p className="text-muted-foreground text-sm">{t("desc")}</p>
      </header>
    </div>
  )
}

export default AdminSettingsPage

3. Add to AdminCP Sidebar Navigation

Export adminNav from src/admin/nav.tsx to display a link in the AdminCP sidebar:

plugins/blog/src/admin/nav.tsx
import type { PluginAdminNav } from "@vitnode/core/lib/plugin"
import { SettingsIcon } from "lucide-react"

export const adminNav: PluginAdminNav = {
  nav: [
    {
      id: "settings",
      icon: <SettingsIcon />,
      href: "/admin/blog/settings",
      // Restrict visibility to authorized staff
      permission: { module: "blog", permission: "can_manage_settings" },
    },
  ],
}

Export the module in package.json:

plugins/blog/package.json
"exports": {
  "./admin/nav": "./dist/src/admin/nav.js"
}

4. Add Localization Messages

Provide translations for the page and sidebar link in src/locales/en.json:

plugins/blog/src/locales/en.json
{
  "@vitnode/blog": {
    "admin": {
      "nav": {
        "settings": "Settings"
      },
      "settings": {
        "title": "Blog Settings",
        "desc": "Configure blog comments, moderation, and notifications."
      }
    }
  }
}

5. Verify the Page

Start the dev server:

bun dev
pnpm dev
npm run dev

Open http://localhost:3000/admin/blog/settings to see your admin screen integrated into the panel.


Gating What the Page Shows

Protect administrative actions inside your component using useStaffPermissions:

plugins/blog/src/routes/admin-settings.tsx
import { useStaffPermissions } from "@vitnode/core/hooks/use-staff-permissions"

const AdminSettingsPage = () => {
  const { hasPermission } = useStaffPermissions()
  const canDelete = hasPermission({ module: "blog", permission: "can_delete_posts" })

  return (
    <div className="p-4">
      {canDelete && <button className="text-destructive">Delete All Posts</button>}
    </div>
  )
}

Learn More