Import
import { render } from "@typed/template/Render";Signatures
export declare const render: {
(where: HTMLElement): <const T extends Renderable.Any>(renderable: T) => Fx.Fx<ToRenderedRenderable<T>, Renderable.Error<T>, Renderable.Services<T>>;
<const T extends Renderable.Any>(renderable: T, where: HTMLElement): Fx.Fx<ToRenderedRenderable<T>, Renderable.Error<T>, Renderable.Services<T>>;
};Why
render makes the root ownership boundary explicit. It connects any
Renderable to one concrete element while preserving the Renderable’s typed
failures and Effect service requirements. The source can be a primitive,
template, Effect, Stream, Fx, or existing RenderEvent.
Ownership and lifetime
Calling render returns an Fx and starts no work. The Scope that runs that Fx
owns dynamic subscriptions and cleanup. The renderer tracks only the rendered
value associated with where; it does not claim the document, overwrite
unrelated class names, or remove nodes outside its dynamic ownership.
DOM updates and hydration
Scalar template parts retain direct targets and update without traversing a
virtual tree. Structural output reconciles only its bounded range. Existing
server-rendered nodes can be hydrated through HydrateContext instead of
replaced, preserving their identity and native browser state.
Examples
import { Effect, Layer } from "effect"
import { html } from "@typed/template"
import { DomRenderTemplate, render } from "@typed/template/Render"
import { Fx } from "@typed/fx"
import { RefSubject } from "@typed/fx/RefSubject"
const program = Effect.gen(function* () {
const count = yield* RefSubject.make(0)
const template = html`<div>
<p>Count: ${count}</p>
<button onclick=${RefSubject.increment(count)}>Increment</button>
</div>`
// Render to document.body
return yield* render(template, document.body).pipe(
Fx.drainLayer,
Layer.provide(DomRenderTemplate),
Layer.launch
)
})
// Can also use pipe syntax
const program2 = Effect.gen(function* () {
const template = html`<div>Hello</div>`
yield* template.pipe(
render(document.body),
Fx.drainLayer,
Layer.provide(DomRenderTemplate),
Layer.launch
)
})Other public imports
These import paths expose the same declaration. Each page retains its own public name and signature.