Browse guides

Control rendering identity and form scope

Keep list identity stable, skip unchanged work and choose the form scope for each field.

The example reorders rows, swaps the selected profile and edits local and validated fields. The comparison below assigns each mechanism to that work.

Table of contents

Main APIs

mount
Starts a Valyrian.js application on a DOM target.
FormStore
Coordinates form state, validation, transforms and submission.
tsx
import { mount } from "valyrian.js";
import { FormStore } from "valyrian.js/forms";

type Row = { id: string; name: string };

const rows: Row[] = [
  { id: "arya", name: "Arya" },
  { id: "sam", name: "Sam" },
];
const localFilter = { query: "" };
const profileForm = new FormStore({
  state: { email: "" },
  schema: {
    type: "object",
    properties: { email: { type: "string", format: "email" } },
    required: ["email"],
  },
  onSubmit: async () => {},
});

const RenderingChoices = {
  selectedId: "arya",

  reverseRows() {
    rows.reverse();
  },

  selectProfile(id: string) {
    this.selectedId = id;
  },

  view() {
    const selected = rows.find((row) => row.id === this.selectedId) || rows[0];
    return (
      <main>
        <button onclick={() => this.reverseRows()}>Reverse rows</button>
        <ul v-for={rows}>
          {(row: Row) => (
            <li key={row.id}>
              <button onclick={() => this.selectProfile(row.id)}>
                {row.name}
              </button>
            </li>
          )}
        </ul>

        <section v-keep={this.selectedId}>
          Selected profile: {selected.name}
        </section>

        <label>
          Local filter
          <input name="query" v-model={localFilter} />
        </label>

        <form v-form={profileForm}>
          <label>
            Validated email
            <input name="email" type="email" v-field={profileForm} />
          </label>
          <button type="submit">Save profile</button>
        </form>
      </main>
    );
  },
};

mount("body", RenderingChoices);

Starting point

Start with a mounted view that contains a reorderable list, one subtree selected by identifier and both a local field and a validated form.

Choose each mechanism by task

key
Use a stable identifier when list order can change so each rendered sibling keeps the same identity.
v-keep
Use the value that represents the subtree input. An unchanged guard skips that subtree during the patch.
v-model
Use it for lightweight local fields whose values live in one local object.
FormStore
Use it when the form needs schema validation, clean or format transforms, submission state or reusable behavior.

Action

Reverse the keyed list and select another profile. Edit the local filter through v-model and the validated email through the FormStore field binding.

Observable result

The list renders in the new order with the same stable identifiers. Changing selectedId allows the guarded profile subtree to render the newly selected name. Each form field writes to its chosen state layer.

Continue with forms

Limit

A stable `v-keep` guard skips that subtree during a patch. Change the guard when the subtree input must render again.

Exact references