Documentation menu

Coming from Lit

Gyral components are standard custom elements, so you can migrate one at a time alongside your existing Lit elements. Gyral uses its own renderer. Its templates are not Lit templates, and Lit directives do not work in them.

A small component

In Lit, a click handler often mutates a reactive property and waits for the component's update. In Gyral, intent names a message, a reducer returns state, and the view renders it:

// src/counter.ts
import { define, html } from '@gyral/core';

type State = { readonly count: number };
type Msg = { readonly _tag: 'Increment' };

export const Counter = define<State, Msg>()('my-counter', {
  init: () => ({ count: 0 }),
  intent: { Increment: true },
  update: { Increment: (s) => ({ count: s.count + 1 }) },
  view: (s, i) => html`
    <button type="button" data-intent=${i.Increment}>Increment</button>
    <output aria-live="polite">${s.count}</output>
  `,
});

There is no event closure in the template. true creates the tag-only message; a parser extracts typed data for a message that needs it. Reducers and views are pure, while commands describe work performed by drivers. define takes two calls: the first names the types, the second the tag and the spec.

Translate responsibilities

Lit concept Gyral equivalent
LitElement subclass define<State, Msg, Props>()(tag, spec) returns a custom-element class
Internal reactive fields / @state Immutable model returned from init and update
Reactive properties / converters Declared prop.string, number, boolean, json or schema-validated value; read ctx.props
Property-change lifecycle logic Optional PropsChanged reducer; keep external props out of state unless synchronization is needed
render() Pure view(state, intents, ctx) returning Gyral html or svg
@click=${handler} data-intent=${i.Name} plus a parser in intent
repeat() each(items, key, row, pick?), with pure rows and explicit changing inputs
classMap / styleMap Explicit attribute/CSS-variable bindings or an application helper; no matching built-in directive
live() for controls Gyral's native live form bindings: value, ?checked, ?selected and textarea text
ref() / element work An explicit element hook, with a server half where markup is needed
Reactive controller / observer A subscription driver for outside sources, or a lifecycle hook with dispose for element-owned work
@lit/task Commands, a driver, and loading/success/failure messages in the model
@lit/context Gyral stores listed in stores and read with ctx.read(store), or props for parent-owned inputs
requestUpdate() Send a typed message or change a declared prop/store; no general manual update API
updateComplete await settled() for the page's pending Gyral rendering
Lit SSR / hydration renderPage or renderToString from @gyral/ssr, with Gyral seeds and native declarative shadow roots

settled() waits for Gyral rendering and outputs, not pending network work or updates inside a Lit element. Await those elements' own lifecycle promises separately. Props default to kebab-case attribute names and do not automatically reflect properties back into attributes. See Components for validation, defaults and synchronization.

Templates and directives

Import html, svg, each, nothing and hooks from Gyral. A template result from Lit is not a Gyral child value. Render a Lit component by its custom-element tag instead:

// src/interop.ts
import { html } from '@gyral/core';

// Import the module defining vendor-chart separately before this template renders.
export const chart = (values: readonly number[]) =>
  html`<vendor-chart .values=${values}></vendor-chart>`;

Gyral's compiler checks its own template grammar. Avoid expressions in tag/attribute names, event bindings, parser-repaired markup and promises in child holes. each rows cannot capture changing state from the view; expose it through pick. Read Views rather than assuming every lit-html directive or binding rule transfers.

Effects, styles and server rendering

Move fetches, timers and listeners out of the view. Return commands from init/reducers for one-off work; use subscriptions(state, props) for sources that should keep running while their inputs remain active. Gyral cancels their lanes on replacement or disconnect. The effects guide shows expected failures and concurrency policies.

Move component styles into styles: css`...` or trusted CSS strings. Gyral shares native constructable stylesheets; expose part names and custom properties as your public theme contract. Existing third-party components keep their own style system.

On the server, register component definitions before rendering or use the component manifest. Commands wait for the client; initial state, props and request-scoped stores are serialized for hydration. Test edited controls and node identity under a production build. You can keep Lit installed for your remaining Lit elements, but Gyral itself does not depend on it. See Server rendering and Using third-party components.

For Lit's original contracts, see its official properties, lifecycle and controllers documentation.