DevelopersRuntime

PWFabric runtime

The execution engine that brings surfaces to life. Handles rendering, state management, and event processing across all environments.

Runtime components

  • Renderer — Transforms surface definitions into rendered output. Supports SSR, CSR, and static generation.
  • State engine — Manages surface state with reactive updates. Supports local, shared, and persistent state.
  • Event system — Handles user interactions and system events. Supports bubbling, capturing, and custom events.

Rendering modes

Server-side rendering (SSR)

Fetch the surface definition server-side and render with SurfaceRenderer. Works with Next.js Server Components.

// Next.js Server Component
import { SurfaceRenderer } from '@pwfabric/runtime'
import { createClient } from '@pwfabric/sdk'
 
const client = createClient({ baseUrl: '...', token: '...' })
 
export default async function Page({ params }) {
  const surface = await client.surfaces.get(params.id)
  return <SurfaceRenderer surface={surface} />
}

Client-side rendering (CSR)

Fetch and render on the client for highly interactive surfaces. Best for dashboard-style experiences.

'use client'
import { useState, useEffect } from 'react'
import { SurfaceRenderer } from '@pwfabric/runtime'
import { createClient } from '@pwfabric/sdk'
 
const client = createClient({ baseUrl: '...', token: '...' })
 
export default function Page({ id }) {
  const [surface, setSurface] = useState(null)
  useEffect(() => {
    client.surfaces.get(id).then(setSurface)
  }, [id])
  if (!surface) return null
  return <SurfaceRenderer surface={surface} />
}

Static generation

For surfaces that don’t change frequently, prefer static generation — fetch at build time and emit pre-rendered HTML. Works with generateStaticParams in Next.js App Router.

Block registry

The runtime maintains a per-World block registry that maps block type identifiers to render components. The default registry is intentionally empty (per ADR-131): blocks arrive on a per-World basis through the pwpack install pipeline (ADR-122). The runtime assembles the registry at request time from the World’s installed pwpacks.

import { SurfaceRenderer } from '@pwfabric/runtime'
import { getBlockRegistryForWorld } from '@pwfabric/runtime/blocks'
 
const registry = await getBlockRegistryForWorld(worldId)
<SurfaceRenderer surface={surface} registry={registry} />

On the server you can build the same registry from a known installation set (useful for tests and previews):

import { assembleCanonicalRegistry } from '@pwfabric/runtime/blocks'
 
const registry = await assembleCanonicalRegistry({
  installedBlocksOverride: ['@pwpack/phiwebs/ui', '@pwpack/phiwebs/data'],
})

The 4 first-party packs (phiwebs-ui, phiwebs-data, phiwebs-forms, phiwebs-nav) together expose 45 blocks. Worlds receive these as their default plan bundle on signup; additional community blocks install through the marketplace.

State management

The runtime exposes a reactive state engine that surfaces can opt into via the state capability. Common patterns:

  • Local state — Block-scoped reactive state (e.g., form inputs)
  • Shared state — Surface-scoped state visible across blocks (e.g., selected tab)
  • Persistent state — User-scoped state that survives page reloads (e.g., theme preference)

Event system

Blocks can emit and listen for events. Standard events include onClick, onChange, onSubmit, plus surface-level lifecycle events (onMount, onUnmount). Custom events bubble through the block tree and can be intercepted by parent blocks.

See also