Best for
- New features or refactors touching pages, routes, actions, components, data, auth, sessions, styling, or tests
- Reviewing WebJs code for correctness or framework usage
- Answering "how should this be structured in WebJs?"
webjsdev/webjs/.agents/skills/webjs/SKILL.md
Build and review WebJs applications. Use when working on WebJs app structure, pages, layouts, routes, server actions, components, signals, data and validation, auth, sessions, styling, the client router, streaming, or tests. WebJs is AI-first, web-components-first, and has no build step.
Decision brief
Use this skill for end-to-end WebJs app work. It helps you choose the right layer first, reach for the right export, and avoid the WebJs-specific mistakes that Next.js or Lit muscle memory causes. WebJs is its own framework: the component API matches Lit and the routing feels li…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/webjsdev/webjs --skill ".agents/skills/webjs"Inspect the Agent Skill "webjs" from https://github.com/webjsdev/webjs/blob/5ac991cea77b29b060b0966f361a8e88b00436c3/.agents/skills/webjs/SKILL.md at commit 5ac991cea77b29b060b0966f361a8e88b00436c3. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
1. Classify the change. Route contract, data model, server mutation, auth, or only UI? 2. Start from the server. Add the page/route and its server action or query before wiring interactive UI. A page render or a POST should already return correct HTML before any component hydrat…
This skill is the quick guide. When you need the full API reference for a surface, load the matching file in references/ (listed below). For even deeper framework detail, WebJs ships buildless, so the source you run IS the source you read: look in nodemodules/@webjsdev/{core,ser…
WebJs is an AI-first, web-components-first framework with no build step: source files are served as native ES modules, and TypeScript is stripped in place (Node 24+ or Bun). It runs SSR + progressive enhancement by default.
New features or refactors touching pages, routes, actions, components, data, auth, sessions, styling, or tests
Classify the task first, then load the smallest useful reference set. Each reference starts with a "What This Covers" section; read that to confirm relevance before reading the rest. Loading more than two or three at once usually means the task is not narrowed yet.
Permission review
The documentation asks the agent to read local files, directories, or repositories.
This skill is the quick guide. When you need the full API reference for a surface, load the matching file in `references/` (listed below). For even deeper framework detail, WebJs ships buildless, so the source you run IS the source you readEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 104 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Use this skill for end-to-end WebJs app work. It helps you choose the right layer first, reach for the right export, and avoid the WebJs-specific mistakes that Next.js or Lit muscle memory causes. WebJs is its own framework: the component API matches Lit and the routing feels like Next, but the execution model is neither.
This skill is the quick guide. When you need the full API reference for a surface, load the matching file in references/ (listed below). For even deeper framework detail, WebJs ships buildless, so the source you run IS the source you read: look in node_modules/@webjsdev/{core,server,cli}/ (each package ships its own AGENTS.md). The complete hosted docs live at https://webjs.dev/docs.
WebJs is an AI-first, web-components-first framework with no build step: source files are served as native ES modules, and TypeScript is stripped in place (Node 24+ or Bun). It runs SSR + progressive enhancement by default.
There is no server/client component split. No RSC render tree, no Flight protocol, no "use client" boundary. Instead:
app/**/page.ts, app/**/layout.ts) run only on the server to produce HTML. They do NOT hydrate, so their own markup cannot be interactive (an @click in a page template is dropped at SSR). They still LOAD in the browser so imported components register.WebComponent custom elements) hydrate per element, islands-style. All interactivity lives here: @event, reactive property assignment, signal mutation.*.server.ts is the one server boundary. With 'use server' its exports are RPC-callable from the client (the import is rewritten to a stub); without it the file is a server-only utility whose browser import throws at load. This, not a component annotation, is how a dependency (the DB driver, secrets, node:*) is kept off the client.route.ts is a server-only HTTP handler (named GET/POST exports), the one routing file that is NOT isomorphic.Progressive enhancement is the default architecture. With JS off, content reads, <a> navigates, and a <form action=${importedAction}> submits to its server action. JS is opt-in per interactive behaviour. Never write a first paint that depends on hydration.
Classify the task first, then load the smallest useful reference set. Each reference starts with a "What This Covers" section; read that to confirm relevance before reading the rest. Loading more than two or three at once usually means the task is not narrowed yet.
| Task involves... | Start with |
|---|---|
| Pages, layouts, dynamic routes, route handlers, metadata, redirects, 404s | references/routing-and-pages.md |
| Writing components: reactive props, signals, lifecycle, light vs shadow DOM | references/components.md |
Server actions, mutations, queries, validation, the ActionResult envelope | references/data-and-actions.md |
Sessions, login flows, route protection, forbidden() / unauthorized() | references/auth-and-sessions.md |
| Tailwind, light-DOM tag-prefix rule, tokens, fixed headers, no-reflow layout | references/styling.md |
| Client router, prefetch, frames, view transitions, Suspense streaming | references/client-router-and-streaming.md |
| Optimistic UI for a user-facing mutation | references/optimistic-ui.md |
The @webjsdev/ui component kit (a components.json is present): class helpers, tokens, add / view, the MCP ui tool | references/ui-kit.md |
TypeScript at runtime, erasable syntax, full-stack types, the derive-the-type rule (never unknown / any) | references/typescript.md |
Unit, browser, e2e tests, the handle() harness, Bun parity | references/testing.md |
Auth, caching, env vars, rate limit, file storage, the webjs config block | references/built-ins.md |
| Node vs Bun, running the app, deploying, runtime-specific differences | references/runtime.md |
| Offline support, an asset cache, the opt-in service worker | references/service-worker.md |
| A pattern that feels like Next.js or Lit but might not transfer | references/muscle-memory-gotchas.md |
Common bundles:
<form> POST should already return correct HTML before any component hydrates.modules/<feature>/), promote to lib/ or components/ only when reuse is real..server.ts. The DB driver, secrets, and node:* never belong in a page, layout, or component.@event) only where the UI is genuinely interactive. A display-only component is elided from the browser.export const validate on an action; the RPC and route() boundaries run it.optimistic() from @webjsdev/core).unknown or any. The row type comes from the schema (typeof todos.$inferSelect), the action's input from a named interface and its result from ActionResult<T>, the routing files from PageProps / LayoutProps / RouteHandlerContext. unknown belongs on a payload nothing has vouched for yet that the next line narrows, and on a parameter of your own helper that forwards into an html template hole. Everywhere else, including a layout's children, it is a missing type. See references/typescript.md.app/ ROUTING ONLY (thin adapters importing from modules/)
layout.ts root layout (the ONLY file that may write <html>/<head>/<body>)
page.ts /
<segment>/page.ts /<segment>
[param]/page.ts dynamic route (params.param)
<path>/route.ts HTTP handler at /<path>
error.ts loading.ts not-found.ts forbidden.ts unauthorized.ts boundaries (nearest wins)
middleware.ts root middleware
modules/<feature>/ actions/ (mutations, *.server.ts), queries/ (reads, *.server.ts),
components/, utils/ (pure), types.ts
lib/ lib/*.server.ts server-only infra, lib/utils/ browser-safe helpers
components/*.ts shared presentational custom elements (one per file)
db/*.server.ts Drizzle: schema, connection
public/* static assets, served at /public/<name>
App-internal imports use the # root alias (import { db } from '#db/connection.server.ts'), Node's native package.json imports field, not deep ../../../ relatives. A same-directory import stays relative.
.server.ts, route.ts, or middleware.ts. Never in a page, layout, or component (it crashes the browser at module load).'use server' exports are async functions returning serializer-safe values. Files without 'use server' are server-only utilities.Class.register('tag-name').@), property (.), and boolean (?) holes in html are UNQUOTED: @click=${fn}, never @click="${fn}".signal / computed from @webjsdev/core, read via signal.get() inside render(). The base-class factory WebComponent({ ... }) is only for values riding an HTML attribute or arriving via SSR hydration.render() themselves.<!doctype> / <html> / <head> / <body>.html\...`` body, even in comments (it closes the literal and 500s).erasableSyntaxOnly: true): no enum, no value namespace, no constructor parameter properties, no legacy decorators.extends WebComponent({ count: Number }). Never a static properties block, never a class-field initializer (it clobbers the reactive accessor).<form action=${importedAction}>, or a per-button <button formaction=${importedAction}> inside a bound form. Quoted bindings, non-submit controls, <input type="submit"> (the identity needs its value, which is also its label, so use a <button>), submitter name / value / form / static formaction attributes, a .prop spelling of any of those, action=${fn} off a <form>, a bound form with method="get", formmethod="get" or an unparseable formenctype on ANY submitter in a bound form, and a non-action function all throw. A page has no action export, so a bare <form method="post"> is a 405.Find the right export fast. Load the linked reference for full examples.
@webjsdev/core (browser + isomorphic)html / css tagged templates. WebComponent({ ... }) base-class factory; prop(type?, opts?) declares one reactive property. register(tag, C) / Class.register('tag').signal / computed reactive state, effect(fn) client-only reaction (returns a disposer), batch(fn) coalesced writes; render(v, el) client render.notFound() / redirect(url[, status]) control-flow throws (page/layout/action only, NOT route.ts). forbidden() / unauthorized() render the nearest boundary.Suspense({fallback, children}) page-level streaming; <webjs-suspense> component-level streaming.optimistic() optimistic UI; navigate(url) / revalidate(url?) client-router control; connectWS / richFetch.asset(path) content-hashes a public/ url so a deploy cannot serve stale bytes (href=${asset('/public/app.css')}), served immutable for a year. Page / layout / metadata route only, inside the render function. See references/built-ins.md.Metadata, PageProps<R>, LayoutProps<R>, RouteHandlerContext<R>, WebjsConfig.@webjsdev/core/server: renderToString / renderToStream (Node side).@webjsdev/core/directives: repeat, unsafeHTML (trusted only), live, keyed, guard, cache, until, watch(signal), ref / createRef, asyncAppend / asyncReplace, templateContent. Task / TaskStatus live at @webjsdev/core/task, context (createContext / ContextProvider / ContextConsumer) at /context. See references/components.md for the directive table + Task + context.@webjsdev/server (server side)createRequestHandler, cors(), route(action, opts?) REST adapter, sitemap() / sitemapIndex(), actionContext(), actionSignal(), requestId(), cache() / revalidateTag.json(v) rich responder, readBody(req), clientIp(req), no-arg headers() / cookies() / cspNonce() (client counterpart richFetch is in @webjsdev/core). See references/routing-and-pages.md.createAuth (+ Credentials / Google / GitHub), auth() / auth(req), session() + cookieSession / storeSession, getSession(req) (.get / .set / .flash / .destroy). File storage: getFileStore / diskStore / signedUrl. See references/auth-and-sessions.md + references/built-ins.md.db/*.server.ts. Auth, sessions, caching, rate limit, file storage are built in and pluggable (references/built-ins.md).page.ts (server-only fn), layout.ts (embeds children), route.ts (HTTP handler), middleware.ts, *.server.ts (server boundary), error.ts / loading.ts / not-found.ts / forbidden.ts / unauthorized.ts (boundaries), metadata routes (sitemap.ts, robots.ts, manifest.ts, icon.ts, opengraph-image.ts).
// app/about/page.ts
import { html } from '@webjsdev/core';
export default function About() {
return html`<h1>About</h1>`;
}
// app/users/[id]/page.ts
import { html } from '@webjsdev/core';
import { getUser } from '#modules/users/queries/get-user.server.ts';
export default async function User({ params }: { params: { id: string } }) {
const user = await getUser(params.id); // never import the DB directly into a page
return html`<h1>${user.name}</h1>`;
}
// modules/users/actions/update-profile.server.ts
'use server';
import { eq } from 'drizzle-orm';
import { db } from '#db/connection.server.ts';
import { users } from '#db/schema.server.ts';
export async function updateProfile(input: { id: string; name: string }) {
const name = String(input?.name || '').trim();
if (!name) return { success: false, error: 'name required', status: 400 };
const [row] = await db.update(users).set({ name }).where(eq(users.id, input.id)).returning();
return { success: true, data: row };
}
Call it from a component via a normal import (rewritten to a typed RPC stub). Never hand-write fetch().
// components/counter.ts
import { WebComponent, prop, html } from '@webjsdev/core';
class Counter extends WebComponent({ count: prop(Number) }) {
constructor() { super(); this.count = 0; }
render() {
return html`<button @click=${() => this.count++}>${this.count}</button>`;
}
}
Counter.register('my-counter');
// modules/contact/actions/send-message.server.ts
'use server';
export async function sendMessage(formData: FormData) {
const email = String(formData.get('email') || '');
if (!email) return { success: false, fieldErrors: { email: 'required' } };
return { success: true, redirect: '/thanks' };
}
// app/contact/page.ts
import { sendMessage } from '#modules/contact/actions/send-message.server.ts';
export default function Contact({ actionData }) {
return html`<form action=${sendMessage}><input name="email"></form>`;
}
Binding the action is the whole wiring: the renderer omits action (so the form posts to the page's own url), supplies method="post" and an enctype, and emits a hidden __webjs_action identity field. A form-bound action always receives the FormData.
Success is a 303 (PRG); failure re-renders the page at 422 with the result on actionData. With JS the client router applies the response in place. A submission that binds nothing is a 405, and the submission is Origin-verified like an RPC call.
Sec-Fetch-Site check on both the action RPC and the form-submit path, not a token cookie. A safe GET action is CSRF-exempt. A route.ts REST endpoint is NOT covered: authenticate every mutating endpoint, validate, rate-limit.ActionResult { success: false, error } envelope, never on a raw throw.forbidden() for an authenticated user lacking permission, unauthorized() for an unauthenticated request. Inside a 'use server' RPC action, return an ActionResult for an auth failure instead of throwing.cors() from @webjsdev/server; credentials: true REQUIRES an explicit origin allowlist, never '*'.handle() from @webjsdev/server/testing and assert on the Response.npm run test:browser) for anything touching hydration, the client router, slots, or custom-element upgrade. A unit test is necessary but NOT sufficient for a browser-facing change.npm run check and npm run typecheck pass even when a layout collapses. Static tools give no signal for a visual defect.node:crypto, the TS stripper) on both..server.ts utility (no 'use server') directly into a shipping component. Its browser stub throws at load; reach it through a 'use server' action.static properties block or a class-field initializer for reactive props instead of the WebComponent({ ... }) factory.@click="${fn}").fetch() to call your own server instead of importing the action.<form method="post"> and expecting a page action export to catch it. There is no such export; bind the action with action=${fn} or the submission is a 405.formaction=${fn} on anything that is not a submit control, or on a button carrying its own name / value. The identity IS the button's name/value pair, so both halves are spoken for.formmethod="get" or formenctype="text/plain" on any button inside a bound form. Neither can carry the action's body, so both are refused even when the button binds nothing.export const method = 'GET'. That is a 405 at runtime and a webjs check error.redirect() / notFound() inside a route.ts handler (uncaught 500). Return a Response there.connectedCallback. SSR does not call connectedCallback; put first-paint data in the constructor (server-known inputs) or use async render().window, document, localStorage) in the constructor or render(). It throws at SSR; do browser-only work in connectedCallback.<style> / <script> body. Use static styles or Tailwind.Alternatives
alirezarezvani/claude-skills
ISO 13485 Quality Management System implementation and maintenance for medical device organizations. Provides QMS design, documentation control, internal auditing, CAPA management, and certification support. Use when working with medical device quality systems, preparing for ISO 13485 audits, managing regulatory compliance documentation, setting up corrective actions, or building audit preparation programs. Useful for quality management, audit preparation, regulatory compliance, medical device d
huggingface/skills
AI demos and GPU compute with Gradio Spaces and Hugging Face Spaces ZeroGPU. Use when writing or reviewing code that uses `@spaces.GPU`, configuring `python_version` or `requirements.txt` for a ZeroGPU Space, or handling ZeroGPU-specific code constraints — pickle-based process isolation, `gr.State` semantics across the worker boundary, no `torch.compile` (use AoTI instead), CUDA wheel-only builds (no `nvcc` at build or runtime), large vs xlarge sizing, and dynamic duration callables. Make sure t
K-Dense-AI/scientific-agent-skills
Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.
K-Dense-AI/scientific-agent-skills
Use when working directly with the `esm` Python SDK, ESM3 or ESMC model IDs, Forge/Biohub inference clients, or ESMFold2 folding workflows.