Skip to content

State

State is one of the core building blocks in Ornata. It is designed to work well with HTML-first markup, progressive enhancement, and direct component updates.

Ornata can build a component’s initial state from:

  • default values in the component definition
  • data-* values on the root HTML element
  • initialState passed to mount()

That makes state flexible in an HTML-first environment because a server template, CMS, or other markup source can provide initial values while JavaScript still refines or overrides them at run time.

When Ornata resolves initial state, later sources win.

The order of priority is (lowest to highest):

  1. Component definition defaults
  2. Root element data-* attributes
  3. initialState passed to mount() [Highest Priority]

That means mount-time JavaScript wins over HTML, and HTML wins over definition defaults.

Ornata automatically maps kebab-case HTML data attributes to your camelCase state properties:

<section data-counter data-count="5" data-label="Server count"></section>
const Counter = defineComponent({
state: {
count: { default: 0, type: Number },
label: { default: 'Clicks', type: String },
},
});
Counter.mount('[data-counter]', {
count: 10,
});

The mounted instance starts with:

  • count = 10
  • label = "Server count"

When state changes after mount, Ornata runs its update flow automatically.

That includes:

  • Validates the new value
  • Recalculates computed values
  • Triggers UI render callbacks
  • Fires matching watch hooks
  • Notifies external state listeners

For the full sequence, see State Update Flow.

This means state updates drive the component directly. To update the state and trigger an update, assign a new value directly like this:

methods: {
increment() {
this.state.count += 1;
},
}

The same goes for external updates on the public state:

const instance = Counter.mount('[data-counter]');
instance.state.count += 1;

Ornata gives you two different ways to react to state changes:

  • watch for internal component-side reactions
  • addStateListener() for external subscriptions on a mounted instance

Use watch when the component itself needs to respond to its own updates.

Use addStateListener() when code outside the component needs to observe public state changes.

Read Watchers and State Listeners for the deeper guidance.

Ornata also protects state access at the component boundary.

Each state property can define:

  • type validates and describes the expected runtime value type
  • private hides the property from external reads and writes on the mounted public state
  • readonly allows external code to read the property but prevents external writes

In practice, those safeguards might look like this:

state: {
count: { default: 0, type: Number },
token: { private: true }, // `instance.state.token` equals `undefined` externally
label: { default: "Clicks", readonly: true }, // `instance.state.label = 'New'` errors and is ignored
}

These options support a few useful guarantees:

  • invalid values can be reported
  • private state cannot be read or written externally
  • readonly state cannot be written externally

When Ornata reads state from HTML, it uses the default and type state properties to infer the expected value type. If it can infer the type, it parses the HTML string into that type automatically. If it cannot, Ornata logs a warning and falls back to treating the value as a string.

Depending on the configuration, Ornata can parse values as string, number, boolean, array, or object. For arrays and objects, the HTML value must be valid JSON.

When the built-in parsing behavior is not a good fit, use parse() to define custom parsing logic. For example, if you want to convert a comma-separated string into an array, you can do this:

<section data-search data-filters="red,blue,green"></section>
state: {
filters: {
type: Array,
parse(value) {
return value.split(",");
},
},
}

Ornata state is:

  • flexible at initialization time
  • direct to update
  • reactive once mounted
  • guarded at the public boundary

That makes it a strong fit for HTML-first environments that need interactive behavior without giving up control over where the initial data comes from.