npm create pocketpages
<!-- pb_hooks/pages/index.ejs -->
<%= `Hello, PocketPages!` %>
Zero compilation, zero bundling, zero build steps. Just write code and deploy - frontend and backend.
No compilation anywhere. Write HTMX, Alpine.js, or VanJS and see changes instantly.
Pages render on the server using PocketBase JSVM. Fast, SEO-friendly, and reliable.
Built specifically for PocketBase JSVM (Goja). Access your data, auth, and APIs directly.
Every new project includes the official PocketPages agent skill. Claude Code, Cursor, Codex, and other agents learn how PocketPages really works: file routing, loaders, layouts, and PocketBase's synchronous JSVM. That means fewer hallucinated fetch calls and async handlers.
npm create pocketpages adds the skill and an AGENTS.md.claude/skills/pocketpages/ # agent skill
AGENTS.md # rules for any agent
CLAUDE.md # imports AGENTS.md
pb_hooks/
pages/
index.ejs
package.json
# Existing project? In Claude Code:
/plugin marketplace add benallfree/pocketpages
/plugin install pocketpages@pocketpages
While SPAs dominate the conversation, MPAs offer proven benefits for most web applications. PocketPages brings this architecture to PocketBase's powerful backend.
<% // Server-side data loading in PocketBase JSVM
const user = $app.findRecordById('users', params.id)
%>
<!-- Template renders with live data -->
<%= user.get('name') %> joined <%= user.get('created') %>
Direct access to your database, authentication, and APIs server-side.
Drop in your favorite no-build frontend library and start building. No webpack configs, no bundlers, no compile steps.
High power tools for HTML
Minimal framework for HTML
Reactive UI with data binding
Ultra-lightweight reactive UI
Pure JavaScript, no dependencies
<%- include('header.ejs') %>
<div hx-get="/api/todos"
hx-trigger="load"
hx-target="#todo-list">
Loading todos...
</div>
<div id="todo-list"></div>
<div x-data="{ todos: <%= JSON.stringify(todos) %> }">
<template x-for="todo in todos">
<div x-text="todo.title"
:class="{ 'completed': todo.done }">
</div>
</template>
</div>
<input type="text"
name="message"
data-bind:message />
<button data-on:click="@get('/api/chat')">
Send
</button>
<div id="chat-box">
<% for (const msg of store('messages')||[]) { %>
<div><%= msg.message %></div>
<% } %>
</div>