Skip to content
forma.
v3.0
☰
Browse documentation
Start here
Build interfaces
Native rendering
Tooling
Security and operations
Architecture
Application guides
API reference

Docs/Security and operations

FORMA 3.0

API stability

Shipped, experimental, deprecated and removed entry points.

View source ↗Markdown ↓Source 753be2a
On this page

What you can build on, what is still moving, and what is known to be broken.

Stable means the API and its observable behaviour are not expected to change, and a change would be a breaking release with a migration note in CHANGELOG.md. Beta means it works and is tested, but the shape may still move. Experimental means do not build on it yet. Deprecated means it still works as documented and is removed in the major its row names; a development build logs a coded warning, and an installed package shows it as @deprecated in its types. See Removed in 3.0.0.

FeatureStatusNotes
Signals (createSignal, createEffect, createComputed, batch)StableCore primitive. Custom equals supported.
Reactive introspection (isSignal, isComputed, trigger, getBatchDepth)Stablealien-signals 3.x type guards
h() / JSX renderingStableFunction components supported
mount(), createShow, createSwitch, createListStable
HTML runtime (data-* directives)StableCSP-safe expression parser; the grammar is a documented subset — see HTML-RUNTIME.md
CSP-hardened runtimeStableNo new Function in the artifact — asserted by scripts/verify-dist.mjs as the last step of npm run build
createStore (deep reactivity)StableMembership changes (Object.keys, spread, for…in) are not reactive by design
Components (defineComponent, lifecycle)Stable
Context (createContext, provide, inject)Stable
Islands (activateIslands, disposal, triggers)Shipped in 3.0.0156 tests across 12 dedicated files †; strict activation validates and adopts compiled islands without initial repair writes. The 3.0.0 release record contains packed qualification; a test count alone is not a coverage or performance guarantee.
createHistory (undo/redo)StableTakes a [get, set] tuple; returns a controls object, not a tuple
createReducerStable
createResource / createSuspenseStableAbortable; the Suspense boundary is captured at resource creation
createPortal, svg(), template()Stable
trustedHtml(), the raw-HTML entry every raw sink takesStableA plain string at a raw sink, and h('script'), are refused: see Deprecated forms.
data-fetch, data-transition:*, data-refStable
Tiered mount (mountMarkup(root, markup, { tier, grants, config }))Shipped in 3.0.0Every call names a tier. Omitted grants give none; explicit fetch, DOM and navigate grants bound the handed root. The handle owns update/disposal. See HTML-RUNTIME.md.
Preview frame runtime (dist/formajs-frame.global.js)BetaNew in 2.1.0: the runtime a server writes inline into the opaque-origin preview frame; renders the module its host sends in tier 'unreviewed'. Its message protocol (module in; ready, resize, error out) remains marked beta.
Tierless HTML runtime lifecycle and automatic mountingRemoved in 3.0.0Use mountMarkup(root, trustedHtml(markup), { tier, grants, config }) and its update/unmount handle. Importing the runtime and DOM changes never bind markup. The obsolete manual attribute grants no authority; development only warns FORMA_W_MANUAL_MOUNT_REMOVED.
TC39 Signals compat (State, Computed)BetaTracks an evolving TC39 proposal
Development build (the development export condition)BetaEach subpath's development condition selects a twin under dist/dev/ that keeps the __DEV__ warnings; the CDN bundles have none. What the warnings say may change in any release. See Development builds.

† The island figure counts literal test declarations (parameterized rows expand to additional runtime cases) in activate, activate-isolation, activate-reactivate, activate-triggers, activate-visible, activate-visible-leak, deactivate, hydrate, hydrate-cleanup, list-hydration, multi-island-integration and shared-signals-across-islands under src/dom/__tests__/.

Verified by src/__tests__/docs-truth.test.ts > "the island coverage figure matches the island test files"

Removed in 3.0.0#

@getforma/core/server, @getforma/core/ssr and @getforma/core/wasm no longer resolve. Call authenticated Rust routes with fetch for server work, render pages with forma-server, and activate compiled islands with activateIslands(). @getforma/build rejects "use server" directives with BUILD-808. The pre-removal API documentation is retained for 2.x users.

Verified by src/__tests__/package-exports.test.ts > "publishes the exact v3 surface without server functions, WASM or Node SSR"

Explicit HTML runtime migration#

The runtime's initRuntime, destroyRuntime, mount, unmount, reconcile, setDebug, setDiagnostics and setDirectiveMap exports are removed in 3.0.0. Use mountMarkup(root, trustedHtml(markup), { tier, grants, config }), handle.update() and handle.unmount(). The component entry's mount() constructs Browser content and refuses server-marked roots; it is not a whole-page hydration API. No automatic scan, body observer or tierless warning path remains. The obsolete data-forma-mount="manual" attribute only produces the development migration hint FORMA_W_MANUAL_MOUNT_REMOVED; production omits that warning.

Verified by src/__tests__/runtime-explicit-mount.test.ts > "exports only explicit mount lifecycle and host inspection helpers" Verified by src/__tests__/runtime-tiered-mount.test.ts > "a production build carries neither the grant report nor the obsolete manual-switch warning"

Development builds#

Development-only warnings sit behind __DEV__: remaining deprecations, props the renderer drops, raw-HTML/children ambiguity and a signal's debug name. Strict hydration refusals and raw-type violations also report in production; they do not disappear when this flag is false. __DEV__ is true in a development build, which is one of:

  • The installed package, resolved with the development export condition. Each subpath's import and require branch has a development key that selects a twin of its file under dist/dev/, built from the same sources with __DEV__ folded to true; the type declarations are shared. Vite's dev server, webpack in development mode, Vitest and Bun's bundler set the condition themselves (Bun's even in a minified build, unless given --production or NODE_ENV=production, and Vitest in every test run unless told otherwise); esbuild, Rollup and Node set it only when told to (conditions: ['development', 'module'], exportConditions, node --conditions=development); @getforma/build sets it in watch mode. See CDN-AND-EXPORTS.md, which has every resolver checked and how to keep a Vitest suite on production.
  • Core compiled from its source: in Node with NODE_ENV other than production; in a browser bundle of the source, which has no process, only with the global __FORMA_DEV__ set.

Everything else is a production build and logs none of these development-only warnings: the installed package resolved without the condition (the files under dist/ but dist/dev/), and every CDN bundle. The CDN bundles have no development build: formajs-runtime.global.js, its hardened and short-named copies, and forma.esm.js exist in their production form only, so a page that loads core from a CDN gets none of these warnings. To see them, install the package and build with a bundler in development.

Verified by src/__tests__/development-build.test.ts > "every subpath resolves to production files without the condition and to their development twins with it" Verified by src/__tests__/development-build.test.ts > "a process resolving with the development condition loads one copy of core, and it is the development build" Verified by src/__tests__/development-build.test.ts > "a process resolving without the condition loads one copy of core, and it is the production build"

Deprecated forms#

The A2 warning bridges are removed in 3.x (DR-19). These forms are refused in development and production before writing to a DOM sink:

FormCodeInstead
A string as dangerouslySetInnerHTML's __htmlFORMA_E_RAW_INNER_HTML{ __html: trustedHtml(markup) }
A string to setHTMLUnsafe()FORMA_E_RAW_SET_HTMLsetHTMLUnsafe(el, trustedHtml(markup))
A string to template() or templateMany()FORMA_E_RAW_TEMPLATEtemplate(trustedHtml(markup))
A string to mountMarkup() or its handle's update()FORMA_E_MOUNT_ARGSmountMarkup(root, trustedHtml(markup), { tier: 'reviewed', grants: {} }) or handle.update(trustedHtml(markup))
A string as a srcdoc valueFORMA_E_RAW_SRCDOCsrcdoc: trustedHtml(markup)
h('script')FORMA_E_SCRIPT_ELEMENTA script in the page, or a module the build bundles
data-bind:srcdoc, or {item…} in a list row's srcdoc, in the HTML runtimeFORMA_E_RUNTIME_SRCDOCThe frame built with h() and trustedHtml(markup)

Each raw sink refuses strings, look-alikes and String objects with a coded TypeError. Native Trusted Types adds browser enforcement; Forma retains the typed boundary in engines without it (../CSP.md). A page that refuses one of core's two policy names (its trusted-types directive does not list it, or another script created it first) makes core write markup as strings, which it then refuses; a development build logs FORMA_W_TT_POLICY_REFUSED once for each name.

Children given beside dangerouslySetInnerHTML (a function, or a value that is not null, undefined or false) log FORMA_W_INNER_HTML_CHILDREN once in a development build, from h() or from adoption. Ordinary h() retains its existing behavior; strict adoption validates matching raw content as opaque and binds none of the children beside non-null markup. An initial null/false value permits fallback children. Compiler raw-content parity is separate qualification; give the element one content source.

Verified by src/dom/__tests__/trusted-html.test.ts > "every raw sink refuses strings before parsing, including an already cached trusted value" Verified by src/dom/__tests__/trusted-html.test.ts > "h refuses script elements in every HTML spelling before construction" Verified by src/dom/__tests__/trusted-html-build.test.ts > "production artifacts retain refusal codes and strip development-only warnings" Verified by src/dom/__tests__/trusted-html.test.ts > "a refused policy name warns once in development, and the sinks then write strings the page refuses" Verified by src/dom/__tests__/adopt-inner-html.test.ts > "children beside the prop warn once in development, in h() and in adoption, and a falsy prop does not" Verified by src/__tests__/runtime-srcdoc.test.ts > "a list row's srcdoc is refused with its binding code"

About the numbers on this page#

There is exactly one, and a test recomputes it from disk on every run: if someone adds or deletes an island test, the build fails until this table agrees. That is the only kind of number worth putting in a document — every other row here is a statement about behaviour, which is what a stability table is for.

PERFORMANCE.md preserves measured historical baselines with their noise floors. The strict-adoption benchmark fixtures have changed; those old timings are not current 3.0 measurements or a cross-version comparison.

Find your next answer.

Search Forma 3.0 documentation, guides and examples.