PocketPages
brew install pockethost/tap/pocketbase
pocketbase pages create myapp
No-Build Multi-Page Apps for PocketBase
<!-- pb_pages/index.ejs -->
<%= `Hello, PocketPages!` %>
Why PocketPages is Better
Zero compilation, zero bundling, zero build steps. Just write code and deploy - frontend and backend.
Zero Build Steps
No compilation anywhere. Write HTMX, Alpine.js, or VanJS and see changes instantly.
Server-Side Rendered
Pages render on the server using PocketBase JSVM. Fast, SEO-friendly, and reliable.
PocketBase Native
Built into PocketBase CE: routing and templates in Go, your page code in PocketBase's JS VM. Your data, auth, and APIs are right there.
Why Multi-Page Apps Matter
While SPAs dominate the conversation, MPAs offer proven benefits for most web applications. PocketPages brings this architecture to PocketBase's powerful backend.
✓ MPA Advantages
- Instant Page Loads: Server renders HTML, browser shows content immediately
- SEO by Default: Search engines get fully rendered HTML
- Progressive Enhancement: Works without JavaScript, enhanced with it
- Simpler Mental Model: Each URL = one page, like the web was designed
- Zero Compilation: No build steps anywhere - frontend or 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') %>
PocketBase JSVM Integration
Direct access to your database, authentication, and APIs server-side.
Perfect for No-Build Frontend
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 ctx.store('messages')||[]) { %>
<div><%= msg.message %></div>
<% } %>
</div>