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":
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:
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 AdminSettingsPage3. Add to AdminCP Sidebar Navigation
Export adminNav from src/admin/nav.tsx to display a link in the AdminCP sidebar:
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:
"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:
{
"@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 devpnpm devnpm run devOpen 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:
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>
)
}