| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
The web framework for AI agents.
WebJs is an AI-first full-stack JavaScript web framework built on web components. It server-renders every page and component to real HTML, needs no build step or bundler, and runs on Node 24+ or Bun.
Nothing is hidden from your agent. The framework ships in node_modules as plain JavaScript, so an agent opens the router or the renderer it is calling instead of recalling an API from training data, and your app code is served to the browser exactly as written. Any model debugs the running app against the real source, with no single blessed model, on the web components and standard HTML every model already knows.
AI-first is a design constraint here, not a marketing tag. File conventions are predictable, each server function lives in its own file, and the .server.ts extension marks the server boundary explicitly, so a coding agent can change one route without loading the whole codebase into context. Every app ships an AGENTS.md contract plus a cross-agent skill that Claude Code, Cursor, Copilot, Gemini, and opencode all read from one source.
It gives you file-based routing, server actions with real end-to-end types, sessions, authentication, caching, rate limiting, WebSockets, and a database layer in the box. cache() for queries, HTTP Cache-Control for pages and for cached GET server actions, a Session class with SessionStorage, NextAuth-style auth with providers, and WebSocket broadcast all share one pluggable store, so a single setStore() call moves them onto Redis with no config files in between.
npm create webjs@latest my-appFull overview at webjs.dev/what-is-webjs. Not the project you were looking for? whatsapp-web.js (often shortened to wwebjs) is an unrelated WhatsApp client library, and an older, unrelated Java framework also used the name WebJS.
AI agents, read this before scaffolding. Only two templates exist (full-stack default, --template api), and the CLI rejects anything else. Always start from one of them. Never hand-roll the directory structure. Default to full-stack with Drizzle + SQLite. Never store app data in JSON files, in-memory arrays, or localStorage as a substitute for the database. The scaffold is reference only. Replace the example layout, page, components, and User model with the app the user actually asked for. Picker:
- Any product UI (todo, blog, notes, dashboard, marketplace, social, e-commerce, a SaaS with accounts…) → default (webjs create <name>)
- Backend-only HTTP/JSON API, no UI → --template api
Auth is one of the full-stack gallery cards (login, a signed session, and a real protected route), so a default app already carries a promotable auth baseline.
The scaffold is a starting point, grown in place. It ships a gallery index home, a neutral-palette root layout, database wiring, and a densely-commented feature gallery (single-concept demos under app/features/ plus the app/examples/todo app, with logic in modules/). The framework context ships as one cross-agent skill, .agents/skills/webjs/SKILL.md (a routing skill), alongside AGENTS.md and .agents/rules/workflow.md. Read the skill and browse the gallery, then build the app the user asked for on top of the scaffold, pruning the demos it does not use.
Full rules: AGENTS.md → How AI agents must scaffold. Full framework docs (every API, every recipe): https://webjs.dev/docs.
# Get started in one command (no global install required)
npm create webjs@latest my-app # full-stack (pages + API + components + Drizzle/SQLite)
cd my-app && npm run dev
# → http://localhost:8080
# Backend-only API (routes + modules + Drizzle, no UI)
npm create webjs@latest my-api -- --template api
# Auth (login/signup, session, a protected route) ships as a gallery card
# in the default full-stack app. No separate template needed.
# Prefer Bun? webjs runs on Node 24+ or Bun. Add --runtime bun to any
# template (it is orthogonal to --template), or scaffold through Bun and
# it is auto-detected. Both forms below produce the same Bun-flavored app.
bun create webjs my-app # auto-detected; runs it with bun --bun run dev
npm create webjs@latest my-app -- --runtime bun # the explicit flag on any package manager
# Or with the CLI installed globally for repeated use.
# `webjsdev` is the unscoped npm name for @webjsdev/cli; both install the `webjs` command.
npm i -g webjsdev && webjs create my-app
cd my-app && npm run dev
# or run everything in the monorepo (website + docs + blog + UI registry together)
git clone https://github.com/webjsdev/webjs
cd webjs && npm install
cd examples/blog && npm run db:migrate && cd ..
npm run dev
# → Website → http://localhost:5001
# → Docs → http://localhost:5002
# → UI registry → http://localhost:5003
# → Blog → http://localhost:5004See Local development for running one app at a time and overriding ports.
packages/ # Framework (the things webjs ships at runtime) core/ # @webjsdev/core: html, css, WebComponent, renderers, client router server/ # @webjsdev/server: dev/prod server, router, SSR, actions, WS cli/ # @webjsdev/cli: webjs dev/start/create/db/test/check/ui intellisense/ # @webjsdev/intellisense: standalone editor intelligence (own template parser, no Lit dep) ui/ # @webjsdev/ui: AI-first component library + CLI # Scaffold entry points (peer wrappers around @webjsdev/cli) create-webjs/ # `npx create-webjs@latest my-app` (mirrors create-next-app) webjsdev/ # unscoped npm name for @webjsdev/cli (so `npm i -g webjsdev` works without a scope) examples/ blog/ # full-featured reference app (auth, posts, comments, chat) gallery/ # the feature gallery every scaffolded app ships, as a live app website/ # landing site AND the documentation at /docs and gallery at /ui AGENTS.md # AI-agent contract for the framework CLAUDE.md # Claude Code quick-reference
Contributing to the framework itself? Run the monorepo's apps from their own dir with npm run dev; as of #550 a bare webjs dev is equivalent (each app's Tailwind compile and prep steps, webjs db migrate, the registry copy, moved into its webjs.dev / webjs.start tasks config, which webjs dev/start run, so the npm scripts are thin aliases). In dev the Tailwind stylesheet is recompiled on request when a source changes (webjs.dev.regenerate, #967), so it never goes stale without a live watcher.
npm install # once, from the repo root (installs every workspace)
npm run dev # all three apps at onceDefault ports (port 5000 is skipped because macOS reserves it for the AirPlay Receiver / Control Center):
| App | Dir | Port | Env override |
|---|---|---|---|
| Landing site, docs, UI gallery | website/ | 5001 | WEBSITE_PORT |
| Example blog | examples/blog/ | 5004 | BLOG_PORT |
| Feature gallery | gallery/ | 5005 | GALLERY_PORT |
Run a single app (from its directory). Each honors a PORT env var:
cd website && npm run dev # landing site + docs on 5001
PORT=8080 npm run dev # ...or on 8080Override ports when running all three via the per-app env vars:
WEBSITE_PORT=8001 BLOG_PORT=8004 GALLERY_PORT=8005 npm run devUse the PORT / *_PORT env vars, not a --port flag. npm run dev --port 5678 does not work: npm parses --port as its own (deprecated) config, and the dev script runs each app through concurrently, which would intercept any passthrough args before they reached webjs dev. The env var is the supported interface (and the conventional one: Railway, Heroku, Fly, etc. all drive port via PORT).
The landing site cross-links to the demo by URL, defaulting to the localhost port above and overridable via EXAMPLE_BLOG_URL (this is also how a deploy points it at the real domain). Nothing else needs a URL: the docs and the UI gallery are served by the landing site itself at /docs and /ui, so they are plain paths.
// app/page.ts: server-rendered, async data fetching
import { html, repeat } from '@webjsdev/core';
import '#components/counter.ts';
import { listPosts } from '#modules/posts/queries/list-posts.server.ts';
export const metadata = { title: 'home' };
export default async function Home() {
const posts = await listPosts();
return html`
<h1>posts</h1>
<ul>
${repeat(posts, p => p.id, p => html`<li>${p.title}</li>`)}
</ul>
<my-counter count="3"></my-counter>
`;
}// components/counter.ts: interactive web component, light DOM + Tailwind
import { WebComponent, html } from '@webjsdev/core';
export class Counter extends WebComponent({ count: Number }) {
// `count` is a reactive property, declared in the base-class factory with
// no `declare` line, so the count="3" attribute on the SSR'd tag seeds it
// and every assignment re-renders. Light DOM is the default, so Tailwind
// utility classes apply directly.
constructor() {
super();
this.count = 0;
}
render() {
return html`
<div class="inline-flex items-center gap-2 font-mono">
<button class="px-3 py-1 rounded border border-border hover:bg-bg-elev" @click=${() => this.count--}>−</button>
<output class="min-w-[2ch] text-center">${this.count}</output>
<button class="px-3 py-1 rounded border border-border hover:bg-bg-elev" @click=${() => this.count++}>+</button>
</div>
`;
}
}
Counter.register('my-counter');Need scoped styles or embed-ready isolation? Opt in to shadow DOM with static shadow = true and author styles via static styles = css\…`. ` projection works in both modes (light DOM uses framework projection, same API).
// modules/posts/queries/list-posts.server.ts: one function per file
'use server';
import { db } from '#db/connection.server.ts';
export async function listPosts() {
return db.query.posts.findMany({ orderBy: { createdAt: 'desc' } });
}No build step. The same source files that ran in webjs dev also run in production, served as native ES modules via importmap, with <link rel="modulepreload"> hints emitted at SSR time so the browser fetches the page's modules in parallel over a single HTTP/2 connection. Same model as Rails 7+ with importmap-rails.
PORT=8080 npm run start # JSON logs, gzip/brotli, ETag, streamingThe production server speaks plain HTTP/1.1. The expected production topology is a reverse proxy in front that terminates TLS and speaks HTTP/2 to the browser. PaaS edges already do this for free. Railway, Fly, Render, Vercel, Cloudflare Pages, and Heroku all serve HTTP/2 to clients while proxying HTTP/1.1 to your container. For bare-VM or self-hosted deploys, put nginx, Caddy, or Traefik in front. HTTP/2 at the edge matters because webjs's per-file ESM model benefits from HTTP/2 multiplex. HTTP/1.1-only deployments still work, just slower on cold cache.
Health: GET /__webjs/health. Graceful shutdown on SIGTERM.
Embed in Express/Fastify/Bun/Deno:
import { createRequestHandler } from '@webjsdev/server';
const app = await createRequestHandler({ appDir: process.cwd() });
const resp = await app.handle(new Request('http://x/api/hello'));The docs are part of the landing site, served at /docs and built on WebJs itself. They live in website/app/docs/, so running the site runs them:
cd website && npm run dev # webjs dev; compiles Tailwind, then recompiles on request (see AGENTS.md)docs.webjs.dev still resolves, as a Cloudflare redirect rule rather than an app in this repo. It sends every request to webjs.dev/docs, the hub page, rather than to the matching page.
The pages cover: getting started, AI-first development, routing, components, SSR, styling, Suspense, loading states, error handling, client router, server actions, REST endpoints, API routes, WebSockets, database, authentication, TypeScript, middleware, rate limiting, lazy loading, metadata routes, caching, sessions, controllers, context protocol, task, deployment, backend-only mode, testing, conventions, configuration, editor setup.
Pre-1.0, released continuously. Current versions of every package (@webjsdev/core, @webjsdev/server, @webjsdev/cli, @webjsdev/ui) are on the changelog. Behaviour is covered by unit, browser (web-test-runner), and puppeteer e2e suites, plus example-app smoke tests. Key features:
MIT
| Back | FazBrowse Home | New Git URL |