Application

defineApp / Application

Back to top

Define pages, socket routes and application services together. Nothing listens until run(); one owner starts and stops the shared HTTP/WebSocket listener.

Options

  • pages: decorated page classes (default [])
  • sockets: SocketRoute classes (default [])
  • services: application-wide classes with onInit(app, signal) and onShutdown() (default [])
  • port: number (default 8181); use 0 for an ephemeral test port
  • bind: string (default 0.0.0.0)
  • httpServices: existing Express service descriptors; route-specific SocketService classes remain on socket routes
  • startupTimeoutMs / shutdownTimeoutMs: total lifecycle budgets (default 5000 each)
  • signals: install owned process signal handling when run begins (default true); embedded applications/tests can set false
  • Existing live-page/HTTP options, including templateRoot, authentication, origins, server (Express app), development and ssl
  • Static export remains exportStatic(); defineApp does not accept a static export flag
  • providers: explicit application-owned objects available to @inject fields on pages
ts
import { defineApp } from 'redweb';
import { HomePage, DashboardPage } from './pages.js';
import { MatchRoute, ChatRoute } from './sockets.js';
import { GameSimulation } from './simulation.js';

const app = defineApp({
  pages: [HomePage, DashboardPage],
  sockets: [MatchRoute, ChatRoute],
  services: [GameSimulation],
});

await app.run();

Methods

defineApp(options) / new Application(options)

Create a deferred definition. Registration arrays are copied; imports and definitions open no port or process handlers.

run(): Promise<Application>

Initialize services in order and bind one HTTP/WS listener. Repeated calls share startup while running. A stopped application cannot restart.

shutdown(): Promise<void>

Cancel pending startup and perform bounded, idempotent cleanup. Close HTTP/socket resources before releasing application services in reverse order. Preserve cleanup errors.

options

Copied definition, available for defining independent test applications with port: 0 and signals: false. Captured objects and supplied Express apps are not deep-cloned.

app / server / http / sockets / services

Runtime Express application, native listener, HTTP adapter, optional WebSocket server and service instances. HTTP members are null before startup; await run before use.

revoke(principal): Promise<number>

Revoke matching live-page sessions; zero before live pages have been initialized.

inspect(): DevelopmentSnapshot | null

Return opt-in live-page inspection metadata, or null when unavailable/disabled.

Read More
HTTP

HttpServer

Back to top

Lower-level Express HTTP listener with parsers, CORS, static assets and services. Prefer defineApp for new applications; use HttpServer when you explicitly need its immediately listening constructor or custom listener ownership.

Options

  • port: number (default 80)
  • bind: string (default 0.0.0.0)
  • publicPaths: string[] (default ["./public"])
  • services: array of { serviceName, method, function }
  • listen: boolean (default true); set false to build app without binding a port
  • listenCallback: function invoked after listen
  • encoding: "json" | "urlencoded" (default json)
  • corsOptions: passed to cors when explicitly configured (default false, including undefined/null; no wildcard CORS). CORS read access does not permit cross-origin unsafe methods.
  • publicOrigin: exact external HTTP(S) origin when a trusted TLS proxy forwards to a private listener; allows same-origin browser mutations from that public origin without trusting arbitrary forwarded headers
JavaScript
import { HttpServer, METHODS } from 'redweb'

const server = new HttpServer({
  port: 4000,
  bind: '0.0.0.0',
  publicPaths: ['./static'],
  services: [
    { serviceName: '/ping', method: METHODS.GET, function: (req, res) => res.json({ pong: true }) },
  ],
})

// Express is still available:
server.app.get('/health', (req, res) => res.send('ok'))

Methods

constructor(options)

Merges defaults, wires body parsing and opt-in CORS, contains static serving inside each public root, registers REST services, and starts listening unless listen is false. Unsafe requests with a foreign Origin or Sec-Fetch-Site other than same-origin/none are rejected; cookie-bearing unsafe requests also require a same-origin signal.

app (Express instance)

Use the returned `app` to add middleware or routes exactly like a normal Express server.

Read More
HTTP

BaseHttpServer

Back to top

Public Express app builder used by HttpServer and HttpsServer. Use it for advanced composition when you want Redweb middleware, static files, and services without any listener behavior.

Options

  • All `HttpServer` app-building options
  • server: existing Express application to configure
  • listen is ignored because BaseHttpServer never binds a port
JavaScript
import { BaseHttpServer, METHODS } from 'redweb'

const base = new BaseHttpServer({
  publicPaths: ['./public'],
  services: [
    { serviceName: '/health', method: METHODS.GET, function: (req, res) => res.json({ ok: true }) },
  ],
})

base.app.get('/extra', (req, res) => res.send('ok'))

Methods

constructor(options)

Builds or configures an Express app with body parsing, CORS, static serving, and REST services.

app (Express instance)

The configured Express application. Pass it to http.createServer(app) for custom server ownership.

Read More
HTTP

HttpsServer

Back to top

TLS-enabled variant of `HttpServer`. Accepts `ssl.key` and `ssl.cert` file paths, wraps them in an https server, and bootstraps the same middleware pipeline.

Options

  • All `HttpServer` options
  • ssl.key: path to private key (required)
  • ssl.cert: path to certificate (required)
JavaScript
import { HttpsServer, METHODS } from 'redweb'

new HttpsServer({
  port: 4443,
  ssl: { key: './certs/dev.key', cert: './certs/dev.crt' },
  services: [
    { serviceName: '/secure', method: METHODS.GET, function: (req, res) => res.json({ ok: true }) },
  ],
})

Methods

constructor(options)

Loads the provided key/cert pair, builds the Express app with BaseHttpServer, then creates and starts the HTTPS listener unless listen is false.

Read More
WebSocket

SocketServer

Back to top

HTTP-upgrade WebSocket server on top of `ws`. Builds and listens on its own HTTP server by default; if you pass a Node `server`, it attaches upgrade handling and leaves `.listen()` to you unless `listen: true` is explicit.

Options

  • port: number (default 3000)
  • listen: boolean (default true for owned servers); supplied servers do not listen unless explicitly true
  • server: existing http.Server to attach to without double-listening (optional)
  • routes: array of SocketRoute subclasses (defaults to a single DefaultRoute at "/")
JavaScript
import { HttpServer, SocketServer } from 'redweb'
import { ChatRoute } from './ChatRoute.js'

// Advanced: attach to a listener whose lifecycle your application owns.
const http = new HttpServer({ listen: false, publicPaths: [] })
const sockets = new SocketServer({ server: http.server, routes: [ChatRoute] })

http.server.listen(3030)

// At application shutdown, detach sockets before closing their shared listener.
async function shutdown() {
  await sockets.shutdown()
  await http.shutdown()
}

Methods

constructor(options)

Creates or reuses an HTTP server, instantiates supplied routes or a DefaultRoute, attaches upgrade handling, and starts listening only when Redweb owns the server or listen is explicitly true.

addRoute(RouteClass)

Instantiate and register another `SocketRoute` at runtime.

handleUpgrade(req, socket, head)

Internal: normalises the request path, picks a matching route (or "/"), and forwards the upgrade to that route's server.

shutdown()

Closes all registered routes and the underlying HTTP server.

Read More
WebSocket

SecureSocketServer

Back to top

HTTPS + WebSocket pairing. Mirrors `SocketServer`, creating an owned HTTPS server from TLS files or attaching to a supplied HTTPS server.

Options

  • port: number (default 3000)
  • listen: boolean (default true for owned servers); supplied servers do not listen unless explicitly true
  • server: existing https.Server to attach to without double-listening (optional)
  • ssl.key and ssl.cert: required file paths only when creating an owned HTTPS server
  • closeServerOnShutdown: defaults to true for owned servers and false for supplied servers
  • routes: array of SocketRoute subclasses
JavaScript
import { SecureSocketServer } from 'redweb'
import { GameRoute } from './routes/GameRoute.js'

new SecureSocketServer({
  port: 3443,
  ssl: { key: './certs/dev.key', cert: './certs/dev.crt' },
  routes: [GameRoute],
})

Methods

constructor(options)

Loads TLS files or reuses a supplied HTTPS server, registers the provided routes, attaches upgrade handling, and starts listening only when Redweb owns the server or listen is explicitly true.

addRoute(RouteClass)

Same runtime route attachment as `SocketServer`.

shutdown()

Stops routes and services. Closes the HTTPS listener only when owned or closeServerOnShutdown is explicitly true; supplied servers remain open by default.

Read More
WebSocket

SocketRoute

Back to top

Defines a WebSocket endpoint and owns its handlers, services, clients, and opt-in multiplayer policies. Routes can add bounded admission, transport limits, ordered work, heartbeat, rooms, resumable sessions, distribution, draining, metrics, and protocol negotiation without changing legacy routes.

Options

  • path: WebSocket path (required)
  • handlers: array of handler classes (required)
  • services: array of SocketService subclasses (optional)
  • allowDuplicateConnections: allow multiple clients from the same IP
  • websocketOptions: options passed to ws WebSocketServer, such as maxPayload or perMessageDeflate; closeTimeout defaults to 5000 milliseconds (integer 1..2147483647) and bounds an already-started closing handshake, not healthy idle connections
  • admission: authenticate, validate origins, and optionally place a client before upgrade
  • maxPendingUpgrades: finite concurrent admission/negotiation work (default 64)
  • limits: connection, message-rate, pending-message, and outbound-buffer ceilings
  • orderedMessages: serialize each connection through a bounded queue
  • heartbeat: one route-level half-open connection monitor
  • rooms and sessions: bounded grouping and expiring application-issued ownership
  • distribution: optional bounded broker adapter; no broker is bundled or required
  • drainHandlers: track handler work and expose a cooperative shutdown signal
  • protocol: version negotiation, stable envelopes/error codes, and optional binary codecs
  • connections: optional connectedClients group for verified player facades and private page projection; enables rooms and handler draining by default
JavaScript
import { SocketRoute } from 'redweb'
import { ChatHandler } from './handlers/ChatHandler.js'
import { ClockService } from './services/ClockService.js'

class ChatRoute extends SocketRoute {
  constructor() {
    super({
      path: '/chat',
      handlers: [ChatHandler],
      services: [ClockService],
      allowDuplicateConnections: true,
      websocketOptions: {
        maxPayload: 1024 * 1024,
        perMessageDeflate: false,
      },
    })
  }
}

Methods

constructor({ path, handlers, services, allowDuplicateConnections, websocketOptions })

Validates input, instantiates handlers/services, sets up a `ws` server for the path with any websocketOptions, and registers connection listeners.

addHandler(HandlerClass)

Adds another handler class unless a handler with the same name already exists.

handleConnection(socket, req)

Stores the client (deduping by IP unless allowed), decorates the socket with `sendJson`/`broadcast`, and wires close/error/message listeners.

handleMessage(socket, data)

Dispatches an already-parsed message object by type. Only literal false declines success; a deliberate rejection should send a correlated protocol error. Unexpected handler exceptions retain sanitized failure and connection-close behavior.

handleBinaryMessage(socket, buffer)

Handles binary frames separately from JSON text frames and delegates to a handler selected by acceptsBinary(socket, buffer).

handleClose(socket, ip)

Removes the client from the registry and triggers an optional `connectionCloseCallback`.

shutdown()

Marks the route draining, stops services, bounds handler/adapter cleanup, closes clients, and releases every route-owned resource.

beginDrain()

Flips readiness and rejects new upgrades before shutdown work begins.

isReady()

Reports whether the route is accepting upgrades and any required distribution adapter is healthy.

publish(type, payload)

Publishes through the optional bounded distribution adapter and resolves to a success boolean.

handleError(socket, error, ip)

Logs socket errors; override for custom reporting.

Read More
WebSocket

SocketService

Back to top

Route-scoped background worker. Used by `SocketRoute` to run ticks or lifecycle hooks tied to a specific route.

JavaScript
import { SocketService } from 'redweb'

class ClockService extends SocketService {
  constructor() { super('clock', 1000) }
  onTick() {
    this.route.clients.forEach((socket) => socket.sendJson({ type: 'time', now: Date.now() }))
  }
}

Methods

constructor(name, tickRateMs = null)

Stores a service id and optional tick interval; the interval is activated in onInit if onTick exists.

onInit(route)

Called once by SocketRoute, sets `this.route` and, if a tick interval was supplied, schedules recurring onTick execution.

onTick()

Optional; implement to run on the configured interval.

onShutdown()

Clears the tick interval; extend for cleanup hooks.

Read More
Multiplayer

FixedStepService

Back to top

Route-scoped simulation clock that compensates for drift, prevents overlapping async ticks, bounds catch-up work, and reports dropped retained lag instead of replaying forever.

JavaScript
import { FixedStepService } from 'redweb'

class Simulation extends FixedStepService {
  constructor() { super('simulation', 50, 3) }
  async onTick(stepMs, tick) {
    await game.update(stepMs, tick)
  }
}

Methods

constructor(name, tickRateMs, maxCatchUpTicks?, maxRetainedLagMs?)

Creates a fixed-step route service with finite catch-up and retained-lag limits.

onTick(stepMs, tick)

Implement one simulation step. Async work never overlaps the next pulse.

onLagDropped(milliseconds)

Optional observability hook called when retained lag is deliberately discarded.

Read More
Multiplayer

RoomRegistry

Back to top

Bounded route-local connection groups. Use asynchronous enterRoom for guarded membership, joinRoom for unguarded rooms, and roomBroadcast for delivery; disconnect cleanup removes all memberships and reclaims empty rooms.

JavaScript
class MatchRoute extends SocketRoute {
  constructor() {
    super({
      path: '/match',
      handlers: [JoinMatchHandler, MoveMatchHandler, ResumeMatchHandler],
      rooms: { maxRooms: 1000, maxMembersPerRoom: 32 },
    })
  }
}

Methods

socket.joinRoom(roomId)

Idempotently joins an unguarded bounded room and returns whether membership is active. Throws when rooms.authorize is configured; use await socket.enterRoom(roomId) instead.

await socket.enterRoom(roomId)

Applies configured room authorization before committing bounded membership and resolves to whether membership is active. Also works for unguarded rooms.

registry.roomsFor(socket)

Returns a snapshot of the socket's current room IDs. Room membership is delivery state, not a durable game seat.

socket.leaveRoom(roomId)

Idempotently leaves one room and reclaims it when empty.

socket.roomBroadcast(roomId, data, options?)

Serializes once and sends to selected connected members.

Read More
Multiplayer

SessionRegistry

Back to top

Bounded, expiring ownership records for application-issued opaque session IDs. Redweb handles takeover and expiry; the application owns credential issuance and payload validation.

JavaScript
class MatchRoute extends SocketRoute {
  constructor() {
    super({
      path: '/match',
      handlers: [JoinMatchHandler, MoveMatchHandler, ResumeMatchHandler],
      sessions: { ttlMs: 30000, maxSessions: 10000 },
    })
  }
}

Methods

socket.createSession(sessionId, data)

Creates a bounded application-issued session owned by the current connection.

socket.resumeSession(sessionId)

Atomically transfers ownership, closes the former owner, and returns stored data.

stop()

Stops the one route-level sweep timer and clears every retained record.

Read More
Client

ProtocolClient

Back to top

Low-level envelope helper from redweb/client. It wraps a socket you own; it does not connect, reconnect or correlate pending requests. For those features use redweb-client; server TSX pages generate their client integration automatically.

ts
import { ProtocolClient } from 'redweb/client'

const socket = new WebSocket('wss://game.example/match?redwebVersion=1')
const client = new ProtocolClient(socket, '1')

socket.addEventListener('open', () => {
  client.send('move', { x: 4, y: 2 }, { sequence: 17 })
})

socket.addEventListener('message', event => {
  try {
    const message = client.parse(event)
    console.log(message)
  } catch (error) {
    console.error('Invalid server envelope', error)
  }
})

Methods

constructor(socket, version)

Wraps any socket-like object with send(data) and selects the envelope version.

envelope(type, payload, metadata?)

Builds a stable versioned event with optional requestId and sequence.

send(type, payload, metadata?)

Serializes and sends one versioned event through the wrapped socket.

parse(value)

Parses and validates a protocol event or error envelope.

Read More
WebSocket

SocketRegistry

Back to top

Small EventEmitter-backed list for socket-scoped entities (players, rooms, etc.). Emits `added` and `removed` events.

JavaScript
import { SocketRegistry } from 'redweb'

const players = new SocketRegistry()
players.on('added', player => console.log('Joined:', player.id))
players.add({ id: 'alice' })
players.remove('alice')

Methods

add(item)

Stores the item and emits an `added` event.

remove(itemOrId, by = "id")

Remove by object reference or by matching a property (defaults to "id"); returns true when removal occurred and emits `removed`.

all()

Returns a shallow copy of all stored items.

count()

Convenience getter for `all().length`.

Read More
WebSocket

BaseHandler

Back to top

Abstract message handler. Provide a name in the constructor; clients send `{ type: name, ... }` to target JSON messages, while binary frames can be accepted and handled as raw Buffer payloads.

JavaScript
import { BaseHandler } from 'redweb'

class UploadHandler extends BaseHandler {
  constructor() { super('upload') }

  onMessage(socket, message) {
    socket.sendJson({ type: 'upload:ready' })
  }

  acceptsBinary(socket, buffer) {
    return buffer.length > 0
  }

  onBinaryMessage(socket, buffer) {
    socket.sendJson({ type: 'upload:chunk', bytes: buffer.length })
  }
}

Methods

constructor(name)

Stores the handler name used by incoming messages.

handleMessage(socket, message)

Calls onMessage; override only if you need pre/post handling logic.

onMessage(socket, message)

Required; implement your message processing here. Throwing will close the socket with an error.

acceptsBinary(socket, buffer)

Optional selector used by SocketRoute to choose a handler for binary frames. Return true when this handler should receive the Buffer.

handleBinaryMessage(socket, buffer)

Calls onBinaryMessage. If onBinaryMessage is not implemented, Redweb sends a "Binary messages are not supported by this handler" error.

onBinaryMessage(socket, buffer)

Optional; override for normal binary-message handling. The second argument is the raw Buffer payload.

onInitialContact(socket)

Optional hook for first-touch logic (not used by default route).

Read More
Utility

sendJson

Back to top

Low-level JSON serialization/send helper. Inside a route, prefer socket.sendJson() so the route’s outbound policy is applied.

JavaScript
import { sendJson } from 'redweb'

// Route handler: respects the route's transport policy.
socket.sendJson({ type: 'ping' })

// Raw socket you own: serialization/send only.
sendJson(rawSocket, { type: 'ping' })

Methods

sendJson(socket, data)

Serialises data and writes it to the socket.

Read More
Constants

ERROR_CODES

Back to top

Stable framework error codes shared by protocol-enabled servers and redweb/client.

JavaScript
import { ERROR_CODES } from 'redweb'

// INVALID_MESSAGE, UNKNOWN_HANDLER, HANDLER_FAILED,
// BINARY_UNSUPPORTED, RATE_LIMITED, QUEUE_FULL,
// CAPACITY_REACHED, INITIALIZATION_FAILED

Methods

Message errors

INVALID_MESSAGE, UNKNOWN_HANDLER, HANDLER_FAILED, and BINARY_UNSUPPORTED.

Capacity errors

RATE_LIMITED, QUEUE_FULL, and CAPACITY_REACHED.

Lifecycle errors

INITIALIZATION_FAILED.

Read More
Constants

SOCKET_OPTIONS

Back to top

Default WebSocket server options used by BaseSocketServer.

JavaScript
import { SOCKET_OPTIONS } from 'redweb'
// { port: 3000, ssl: null, listen: true, routes: [] }

Methods

port

Default WebSocket port (3000).

ssl

Default TLS config (null).

listen

Owned socket servers listen by default. Supplied servers remain caller-owned unless listen is explicitly true.

routes

Routes array default (empty; a DefaultRoute is created when none are provided).

Read More
Constants

HTTP_OPTIONS

Back to top

Frozen defaults used by the HTTP and HTTPS server constructors.

JavaScript
import { HTTP_OPTIONS } from 'redweb'
// port 80, bind 0.0.0.0, publicPaths ['./public'],
// listen true, encoding 'json', and safe error exposure disabled

Methods

Server defaults

port, bind, listen, ssl, logger, and static public paths.

Application defaults

services, encoding, CORS, and safe error exposure.

Read More
Constants

ENCODINGS

Back to top

Supported request-body parser names for HTTP server configuration.

JavaScript
import { ENCODINGS } from 'redweb'
// ENCODINGS.json, ENCODINGS.urlencoded

Methods

json

Use Express JSON body parsing.

urlencoded

Use Express URL-encoded body parsing.

Read More
Constants

METHODS

Back to top

Lowercase HTTP verb helpers passed straight to Express route registration.

JavaScript
import { METHODS } from 'redweb'
// METHODS.GET, METHODS.POST, METHODS.PUT, METHODS.DELETE

Methods

GET

Use with services array or Express: METHODS.GET

POST

Use with services array or Express: METHODS.POST

PUT

Use with services array or Express: METHODS.PUT

DELETE

Use with services array or Express: METHODS.DELETE

Read More
WebSocket

BaseSocketServer

Back to top

Shared lifecycle and route-composition base for SocketServer and SecureSocketServer. Prefer defineApp for application startup; use concrete server classes or this base for infrastructure integrations.

JavaScript
import { BaseSocketServer } from 'redweb'

// SocketServer and SecureSocketServer inherit:
// addRoute(), handleUpgrade(), beginDrain(), and shutdown().

Methods

addRoute(RouteClass)

Creates and registers one route class while enforcing unique paths.

beginDrain()

Stops accepting new upgrades across every registered route.

shutdown()

Drains routes and closes owned listeners without closing caller-owned servers.

Read More
Live HTML

LiveHtmlServer

Back to top

Lower-level server-rendered page host. Prefer defineApp({ pages, sockets }) and app.run() to compose a new application; LiveHtmlServer remains available for direct page-host lifecycle integration.

Options

  • pages: non-empty array of classes decorated with page()
  • templateRoot: optional root for colocated HTML and CSS assets
  • sessionTtlMs and maxSessions: bound pending and reconnectable sessions
  • maxConcurrentRenders: independent HTTP render concurrency ceiling
  • heartbeat: detects half-open browser connections
  • authenticate: binds HTTP renders and socket upgrades to one stable identity
  • origins: exact allowlist or asynchronous origin predicate
  • providers: explicit objects for @inject fields
  • uploadTimeoutMs: bounds an in-progress browser file action
TypeScript
import { LiveHtmlServer } from 'redweb'
import { DocsPage, StatusPage } from './pages.js'

const server = new LiveHtmlServer({
  pages: [DocsPage, StatusPage],
  port: 8080,
  heartbeat: { intervalMs: 15_000, timeoutMs: 10_000 },
})

Methods

constructor(options)

Builds the page renderer, HTTP routes, generated assets, and live WebSocket route on one listener.

shutdown()

Aborts active renders, drains routes, disposes pages and components, and closes owned resources.

Read More
Live HTML

LivePage and start

Back to top

Pages are plain decorated classes; extending LivePage is optional. Prefer defineApp({ pages: [...] }) and app.run() for startup. start() remains a supported lower-level convenience.

TSX
import { action, defineApp, page, state } from 'redweb';

@page('/', { shared: true })
class CounterPage {
    @state() count = 0;

    @action()
    increment() { this.count += 1; }

    render() {
        return <button rw-click="increment">Count {this.count}</button>;
    }
}

const app = defineApp({ pages: [CounterPage] });
app.run();

Methods

start(PageClass, options?)

Starts one decorated page, or an array of pages, with the concise Live HTML server API.

loading(context)

Optional cancellable hook that runs before the initial server render.

connected(context)

Optional hook that runs after the authenticated live socket connects.

disconnected(context)

Optional hook for stopping connection-owned timers and subscriptions.

disposed()

Optional idempotent final cleanup hook for pages and components.

Read More
Live HTML

page, component, state, action, view

Back to top

Small TypeScript decorators declare routes, reusable component ownership, reactive server state, browser-callable actions, and collection item views.

TSX
import { action, defineApp, page, state } from 'redweb';

@page('/', { shared: true })
class CounterPage {
    @state() count = 0;

    @action()
    increment() { this.count += 1; }

    render() {
        return <button rw-click="increment">Count {this.count}</button>;
    }
}

const app = defineApp({ pages: [CounterPage] });
app.run();

Methods

page(path, options?)

Registers a unique route plus template, CSS, sharing, metadata, caching, and live/static behavior.

component()

Marks a class as a reusable state/action/lifecycle namespace.

component(render)

Creates a concise synchronous function component for stateless reusable HTML.

state(options?)

Publishes reassigned values; writable state may also receive bounded browser input.

action({ input? })

Explicitly exposes one method to rw-click or rw-submit. An optional Standard Schema input validates and transforms one submitted argument before invocation; ActionInput<typeof schema> describes its output. Invalid input stays recoverable, while validator bugs remain server failures. Undecorated methods stay unreachable.

rw-status="action"

Optional component-scoped placement for built-in action feedback. Without a slot, buttons/forms get an automatic status message. Pending duplicates from one control are suppressed; late responses preserve changed drafts and replacement forms. Disconnected actions are never queued or replayed.

view(stateName)

Renders each item of one decorated array for an rw-each collection.

inject(provider)

Assign an explicitly registered application provider to a page field; no hidden container or constructor injection.

liveResource() / resource(source, select)

Publish a value for a key and project it to matching active pages or components.

upload({ maxBytes?, accept? })

Expose a bounded streaming file action for rw-upload or rw-paste; applications own storage and validation.

Read More
Live HTML

JSX rendering

Back to top

Dependency-free server-side TSX that renders directly to HtmlFragment values. It provides readable components, fragments, arrays, automatic escaping, safe attributes, and existing html-fragment interoperability without React, a virtual DOM, or hydration.

Options

  • TypeScript: jsx = react-jsx and jsxImportSource = redweb
  • Production runtime: redweb/jsx-runtime; development runtime: redweb/jsx-dev-runtime
  • External CSS and rw-* server directives replace inline styles and browser event functions
TSX
// Start with npx redweb init; keep this source in a .tsx file.
import { defineApp, page } from 'redweb'
import type { Child } from 'redweb/jsx-runtime'

function Card({ title, children }: { title: string; children?: Child }) {
  return <article><h2>{title}</h2>{children}</article>
}

@page('/', { live: false })
class HomePage {
  render() { return <Card title="Redweb">Readable server TSX</Card> }
}

const app = defineApp({ pages: [HomePage] })
app.run()

Methods

Intrinsic elements

Serialize standard, SVG, custom, data-*, aria-*, and rw-* attributes with HTML-correct boolean handling.

Fragments and arrays

Compose nested fragments and readonly child arrays without wrapper markup or comma coercion.

Function components

Synchronous functions receive typed props and children and must return an HtmlFragment or fragment array.

Escaping and URLs

Text and attributes escape automatically; URL attributes retain Redweb’s safe-protocol validation.

Interoperability

Existing html fragments nest in TSX and TSX fragments nest in html for incremental migration.

Read More
Live HTML

html, attribute, url, each, codeBlock

Back to top

Safe composition primitives escape text and quoted primitive attributes by default. URL attributes additionally reject executable, protocol-relative, and malformed values. Arrays must contain trusted HtmlFragment values.

TypeScript
import { codeBlock, each, html } from 'redweb'

const links = sections.map(section => ({
  id: section.id,
  href: `#${section.id}`,
  label: section.name,
}))

const navigation = each(links, link => html`
  <a id="${link.id}" href="${link.href}">${link.label}</a>
`)

// TS, TSX and JS highlight automatically, including rw-* action references.
const example = codeBlock(source, { language: 'tsx', label: 'TypeScript' })

Methods

html`...`

Creates an HtmlFragment and escapes every ordinary interpolation.

attribute(value)

Optionally brands a primitive for a quoted, non-URL attribute when explicit intent improves readability.

url(value)

Optionally brands a safe relative, HTTP, HTTPS, mail, or telephone URL; direct strings receive the same validation.

each(items, render)

Validates and joins a mutable or readonly list of HtmlFragment results.

codeBlock(code, options?)

Builds an escaped figure/pre/code fragment with automatic JS/TS/TSX highlighting; custom highlighters and opt-out remain available.

highlightCode(source, language)

Returns safely escaped token markup, including identifiers inside rw-* JSX action expressions.

Read More
Live HTML

defineSite

Back to top

Defines shared static-site CSS, metadata, caching, layout, canonical URLs, and export behavior once. Site pages are always runtime-free.

Options

  • origin: optional HTTP(S) origin used to derive canonical and root-relative social-image URLs
  • css, head, cache, and layout: defaults inherited by every site.page() decorator
  • layout: synchronous function receiving the trusted page fragment and portable request context
  • publicDir: optional link-free asset tree staged with generated output
TSX
import { defineSite } from 'redweb'

const docs = defineSite({
  origin: 'https://example.com',
  css: 'site.css',
  head: { description: 'Product documentation', image: '/og.png' },
  cache: { maxAge: 300 },
  layout: content => <body><nav>Product</nav><main>{content}</main></body>,
})

@docs.page('/docs', { head: { title: 'Documentation' } })
class DocsPage {
  render() { return <h1>Documentation</h1> }
}

await docs.export(DocsPage, {
  outDir: 'dist',
  publicDir: 'public',
})

Methods

site.page(path, options?)

Creates a non-live page decorator while merging shared defaults and page-specific overrides.

site.export(pageOrPages, options)

Stages all pages and public assets, rejects path collisions, then writes the destination and returns every output path.

Read More
Live HTML

exportStatic

Back to top

Renders non-live decorated pages to deterministic directory indexes and content-addressed CSS. It is intended for docs, marketing pages, and static hosting.

Options

  • outDir: required output directory; existing unrelated files are preserved
  • templateRoot: optional explicit root for templates and CSS
  • logger: optional framework logger or null
TSX
import { exportStatic, page } from 'redweb'

@page('/docs', { live: false })
class DocsPage {
  render() { return <h1>Redweb docs</h1> }
}

await exportStatic(DocsPage, { outDir: 'dist' })

Methods

exportStatic(pageOrPages, options)

Returns frozen page and asset path lists after every page renders and its CSS is written.

Read More
Live HTML

HtmlRenderer

Back to top

Lower-level rendering utility. Most applications should use plain @page classes with defineApp, or defineSite for static sites; this surface supports tooling and advanced integrations.

TypeScript
import { HtmlRenderer } from 'redweb'

const markup = HtmlRenderer.render(
  '<h1>{{ title }}</h1>',
  { title: 'Reference' },
  { live: false },
)

const document = HtmlRenderer.document(markup, null, [], {
  title: 'Reference',
})

Methods

render(source, page, options?)

Renders declarative bindings and collection views against a page object.

document(markup, config?, stylesheets?, metadata?)

Wraps markup in a complete document and injects metadata, CSS, and optional live bootstrap.

template() / stylesheet()

Loads a validated page asset inside an explicit root.

statePayload()

Builds the text or trusted-HTML state payload used by live updates.

Read More
WebSocket

defineSocketContract

Back to top

One shared Standard Schema contract validates wire payloads and infers client/server types. Route URLs choose the service; individual handler factories dispatch by message type.

ts
import { defineSocketContract } from 'redweb/contract';
import { z } from 'zod';

const position = { x: z.number().int().min(-100).max(100), y: z.number().int().min(-100).max(100) };

// Share this module with a browser or Node client. It imports no server application code.
export const match = defineSocketContract('1', {
    join: z.object({ name: z.string().trim().min(1).max(40) }).strict(),
    move: z.object(position).strict(),
    resume: z.object({ session: z.string().uuid() }).strict(),
    state: z.object({ session: z.string().uuid(), name: z.string(), ...position }).strict(),
});

Methods

defineSocketContract(version, schemas, options?)

Creates an immutable contract and negotiated protocol policy from Standard Schema validators. Redweb does not require a particular validator at runtime.

handler(type, callback)

Creates a BaseHandler subclass. Validation completes before the callback receives its typed payload. The returned class can bind rw-submit or rw-click on a socket-bound page; Handler.with(payload) snapshots typed input.

client(socket)

Wraps an existing WebSocket with typed, validated send and parse methods. It does not open or reconnect the connection.

send(socket, type, payload, metadata?)

Validates server output before sending through the normal transport and protocol policy.

Read More
WebSocket

connectedClients / ConnectedClient

Back to top

Route-owned connected players with checked identity, bounded room membership, deduplicated presence and private server-page updates. Reuses RoomRegistry; no second socket map or browser dispatcher is required.

Options

  • identity: rechecks the admitted principal against current credentials
  • page: lazy reference to the attached page class
  • project: side-effect-free per-player page state; online identities are deduplicated
  • reject / errorState: classify deliberately public errors and map them to page fields
  • raw: optional update/reject adapters for non-HTML clients
  • authorizationTimeoutMs / projectionTimeoutMs: bounded asynchronous work
ts
import { connectedClients } from 'redweb'

// Supply your authenticated account lookup, game service, page and contract.
const players = connectedClients({
  identity: context => account(context.request),
  page: () => GamePage,
  project: (player, room, online) => ({
    game: games.resume(room, player.identity).snapshot(player.identity),
    online,
  }),
})
const match = players.bind(contract)
const Join = match.handler('join', (player, { room }) =>
  player.join(room, () => games.join(room, player.identity)))

// Register connections: players and protocol: match.protocol on MatchRoute.
// Attach GamePage with @page('/', { socket: MatchRoute }).
// Its TSX form uses rw-submit={Join}.

Methods

players.bind(contract)

Creates typed handlers whose callback receives a ConnectedClient; successful commands refresh its rooms.

players.get(socket)

Returns the checked route-owned facade for page connection lifecycle hooks.

players.refresh(room)

Projects external domain changes to that room’s currently authorized connections.

player.identity / page / rooms / room

Admitted identity, private page, membership snapshot, and exactly-one-room convenience accessor.

player.join(room, commit?) / leave(room)

Checks capacity and authorization before a synchronous domain commit; failure removes newly added membership, not arbitrary domain side effects.

Read More

Built with Redweb TSX static rendering. No React, client framework, or browser runtime.