plainweb is a set of tools, documentation, and design principles for building simpler web apps.
It consists of three parts:
create-plainweb
: A CLI for creating a starter projectplainweb
: A small NPM package that integrates existing tools and packagesI've built plainweb for myself and other Grug Brained Developers.
I wanted something that:
.tsx
componentsThe term "framework" can be intimidating. Why would you lock your business-critical code into yet another JS framework?
plainweb isn't a framework like Rails, Django, or Laravel. The plainweb
NPM package is tiny, and our long-term goal is to eliminate it entirely! Underneath this thin layer is a set of battle-tested tools, each with a small API surface area.
The documentation is the most crucial part of the plainweb framework. create-plainweb
(starter) and plainweb
(library) provide the building blocks, while the docs show how to compose them.
Once plainweb doesn't meet your needs anymore, it's easy to transition away. All you need is a solid understanding of the following tools:
Simplicity is a trade-off. We have to say "no" to certain things.
SQLite eliminates the need for a separate database process. It uses 5 datatypes and has a small API surface area. You don't need a database administrator or a managed service.
However, SQLite lacks some of the advanced features found in Postgres and other databases.
Deploying and operating a single long-running Node.js process provides a simple mental model. You're less likely to encounter edge cases common in Node-compatible serverless runtimes.
The trade-off is that this traditional model makes it more challenging to deploy to multiple regions or scale horizontally.
plainweb strongly discourages frontend build processes. Instead, HTMX and Alpine.js are embraced to progressively enhance server-side rendered HTML.
While this approach simplifies development, it makes it harder to leverage existing frontend ecosystems like React.
Node.js is a mature and reliable runtime. It's been around for a while and edge cases have been ironed out.
Bun is generally faster than Node.js and plainweb might switch to Bun once it reaches a stable state. Bun's file router and built-in SQLite support would allow us to remove these bits from plainweb.
Express is a mature and well-tested framework with a rich middleware ecosystem. Since plainweb doesn't support multiple deployment targets like Hono, it can fully lean into the Node.js ecosystem.
Express receives less attention from the community, and some middleware packages are not actively maintained. Note that plainweb may switch to Hono in the future.
plainweb is the right tool for you if you:
plainweb is probably not the right tool for you if you:
plainweb draws inspiration from and builds upon the ideas of the Grug Brained Developer, SQLite, HTMX, PocketBase, Remix, Litestream, and some of the Go proverbs.
To get a rough idea of the API surface area of plainweb
, here is the complete API:
// core
export { defineConfig } from "./config";
export { log, getLogger } from "./log";
export { randomId } from "./id";
// http & web
export { fileRouter } from "./file-router";
export { type Handler } from "./handler";
export { html, json, redirect, stream, notFound } from "./plain-response";
export { printRoutes } from "./print-routes";
export { middleware, defineMiddleware } from "./middleware";
export { testHandler } from "./test-handler";
export { getApp } from "./get-app";
// mail
export { outbox, sendMail } from "./mail";
// database
export { getDatabase } from "./get-database";
export { isolate } from "./isolate";
export { migrate } from "./migrate";
// tasks
export { perform } from "./task/task";
export { getWorker } from "./get-worker";
export { defineDatabaseTask } from "./task/database";
export { defineInmemoryTask } from "./task/inmemory";
// unstable
import { adminRouter } from "./admin/admin";
export const unstable_admin = adminRouter;