Browse documentation

Template authoring

Authoring Typed templates

Extract the completed search field into reusable markup while keeping its state with the page.

The first template owns its query inside the field’s view. Extract the field so a parent page can share that query with other output. The page will own state; the field will own its markup and input handler.

NeedUsePrimary contract
Extract reusable markupTemplate functions and componentsUse a component when setup needs an Effect lifetime.
Bind an attribute, property, or booleanElement bindingsTarget the browser field the value actually owns.
Render changing collectionsKeyed collectionsStable keys retain each item’s DOM and owned work.
Pass a record of element bindingsSpreads and dataEach spread entry selects its own binding kind.
Access a mounted elementReferencesElement work belongs to the mounted lifetime.
Choose DOM, HTML, or hydrationRendering and hydrationRendering and browser attachment have distinct owners.

Make the state boundary match the reusable component

import { RefSubject } from "@typed/fx";
import { component, html } from "@typed/template";
import * as EventHandler from "@typed/template/EventHandler";

const SearchField = (query: RefSubject.RefSubject<string>) => {
  const readQuery = EventHandler.make((event: Event) =>
    RefSubject.set(query, (event.currentTarget as HTMLInputElement).value),
  );

  return html`<label>
    Search terms
    <input type="search" .value=${query} oninput=${readQuery} />
  </label>`;
};

export const SearchPage = component(function* () {
  const query = yield* RefSubject.make("");

  return html`<main>
    <h1>Saved articles</h1>
    ${SearchField(query)}
    <output>Current query: ${query}</output>
  </main>`;
});

The field needs no yielded setup, so a direct template function is sufficient. The parent uses component to yield state creation and return a template. Neither adds a wrapper element around the child’s label. The running Effect scope owns the state and subscriptions; constructing either value starts no fiber. Mounting DOM output supplies that lifetime.

For other setup and output choices, see Component and What a template can render.

Name the browser surface you intend to change

The same state can drive different native operations. Choose syntax by the operation, rather than using one generic property record for everything:

Desired changeTemplate formWhat Typed owns
Display the query as text${query}one text position or dynamic range
Set a control’s current edit buffer.value=${query}the live value property
Describe expansion to assistive technologyaria-expanded=${expanded}one serialized attribute
Enable or disable a control?disabled=${disabled}the boolean attribute’s presence
Run work from an input eventoninput=${handler}a scoped native registration

An attribute containing "false" and an absent boolean attribute mean different things. See Attributes, properties, and boolean state for the set and clear rules. Classes, data records, and element references have dedicated binding forms.

Continue with Handle native events with Effect to read browser event data, then Change a keyed template collection to preserve item identity as a list changes.