Builds PocketBase superuser admin UI extensions (admin plugins) on PB ≥0.37: ServeEvent.uiExtensions, client main.js, window.app SPA hooks, Shablon UI, reactive stores, CSP, SSE/realtime topics. Use when extending the PocketBase admin dashboard, operator pages, custom collection tabs/fields, live dashboards, or mothership post-v0.39 operator tooling — not for pb_hooks server logic alone (see pocketbase-jsvm) or external SPAs.
87%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
PocketBase ≥0.37 (including mothership 0.39.*) ships a rewritten superuser dashboard with an experimental UI extension system. Extensions add pages, header links, collection tabs, field types, modals, and DOM hooks inside the built-in admin SPA.
Status: APIs are experimental and undocumented on pocketbase.io until Stage 2 / v1.0-rc. Expect breaking changes. Source of truth: GitHub discussion #7612.
| Layer | Runs in | Purpose |
|---|---|---|
| Server registration | JSVM (pb_hooks) or Go OnServe | Mount static assets at /_/extensions/{name}/*, bundle main.js into /_/extensions.js |
| Client registration | Browser (admin SPA) | main.js uses global window.app to register routes, store fields, modals, mount hooks |
pb_hooks/register.pb.js extensions/my-plugin/
│ ├── main.js ← client entry (required for UI hooks)
│ onServe → uiExtensions ├── style.css
└──────────────────────────────└── …
│
▼
GET /_/extensions/my-plugin/*
GET /_/extensions.js ← concatenates all main.js files
│
▼
Admin SPA loads extensions.js → window.app hooks fire| PocketBase | Admin plugins |
|---|---|
| ≤ v0.36 | Not available (old Svelte admin, no extension API) |
| ≥ v0.37 | Experimental UI extensions |
| Mothership 0.39.* | Supported (PocketHost control plane) |
Confirm target version before writing extension code. Customer instances must run ≥0.37 to load admin plugins.
| Task | Skill |
|---|---|
Register extension dir, onServe, static FS | pocketbase-admin-plugins (this) + server-registration.md |
Client pages, window.app, Shablon | pocketbase-admin-plugins + client-api.md |
| Reactive UI, maps, CSP, imperative DOM | shablon-reactivity-and-dom.md |
| SSE, custom topics, collection subscribe | realtime-and-sse.md |
| Custom API routes, record hooks, cron | pocketbase-jsvm |
| External customer-facing dashboard | pocketbase-js-sdk + separate SPA |
Place static assets next to hooks (paths are your choice; keep them stable across deploys):
pb_hooks/
admin_plugins.pb.js
pb_admin_ext/
hello/
main.js
style.css// pb_hooks/admin_plugins.pb.js
$app.onServe().bindFunc((e) => {
e.uiExtensions.push({
name: 'hello',
fs: $os.dirFS(`${__hooks}/../pb_admin_ext/hello`),
})
e.next()
})Each name becomes the URL segment /_/extensions/{name}/…. Invalid entries (empty name or missing FS) are skipped.
main.js)Runs in the browser (modern JS, async/await OK). Not Goja JSVM rules.
// pb_admin_ext/hello/main.js
app.store.headerLinks.push({
href: '#/hello',
icon: 'ri-hand-heart-line',
label: 'Hello',
})
app.routes.superuserOnly('#/hello', () => {
return t.div({ className: 'page' }, t.h1(null, 'Hello from an admin plugin'))
})Open the mothership/instance admin UI, sign in as superuser, click the new header link.
name is unique, URL-safe (used in /_/extensions/{name}/)main.js exists at FS root if you need client hooks (optional for static-only assets)main.js use /_/extensions/{name}/… pathse.next() in onServeapp.pb (superuser SDK) for data, not raw tokens in sourceonclick (lowercase), not onClickstore() + arrow children () => …, not plain objectsapp.modals.openRecordUpsert(collection, …) with collection object from app.store.collectionsThe admin UI uses Shablon (t.div, t.button, …). Shablon is optional in extensions. Plain DOM works:
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = '/_/extensions/hello/style.css'
document.head.appendChild(link)Important: Shablon is not Mithril. oncreate / data-* attrs / ref callbacks are unreliable for plugin routes. Use id + imperative DOM for maps and other third-party widgets. See shablon-reactivity-and-dom.md.
Explore live APIs: open admin UI → DevTools → console.log(app).
Extensions run with full superuser privileges in the browser and serve from public /_/ routes (CSP applies). Do not ship untrusted third-party extension code. PocketBase will warn about this in future docs (#7612).
Mothership (0.39) and customer instances (≥0.37) differ in deploy path. See pockethost.md.
Typical mothership use case: Live operator dashboard (pb_admin_ext/live/) — platform SSE topics, edge collection subscribe, Leaflet heatmaps. See realtime-and-sse.md and shablon-reactivity-and-dom.md.
Other use cases (backlog): operator stats pages replacing legacy SQL views — view collections + admin plugin charts/tables.
| File | Contents |
|---|---|
| server-registration.md | JSVM + Go registration, FS layout, routes |
| client-api.md | window.app — routes, store, modals, field types, mount events |
| shablon-reactivity-and-dom.md | Reactive stores, ticks, CSP, imperative DOM / Leaflet |
| realtime-and-sse.md | Collection subscribe, custom SSE topics, server broadcast |
| examples.md | Copy-paste patterns |
| pockethost.md | Mothership tsdown, phio/FTP deploy, dev workflow |
ServeEvent.uiExtensions: https://pocketbase.io/jsvm/interfaces/core.ServeEvent.htmlUIExtension: https://pocketbase.io/jsvm/interfaces/core.UIExtension.htmlpockethost/pockethost
8ac1660
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.