Skip to content

Getting Started

This guide takes you from installation to a working progressively enhanced component in an HTML-first environment.

Terminal window
npm install ornata

If you want to enhance a page without a bundler, load the browser build from a CDN instead:

<script src="https://cdn.jsdelivr.net/npm/ornata@latest/dist/index.global.js"></script>
<script>
const { defineComponent } = window.Ornata;
</script>

Pin a specific version in production so your deployed HTML always gets the same runtime.

Write server-rendered markup that already makes sense on its own.

Ornata treats that markup as the integration surface, then layers a reusable component on top.

<section data-counter>
<h2>Counter</h2>
<p>
Count:
<span data-count-value>0</span>
</p>
<button type="button" data-count-button>Increment</button>
</section>
import { defineComponent } from "ornata";
const Counter = defineComponent({
name: "Counter",
state: {
count: { default: 0 },
},
elements: {
value: { query: "[data-count-value]" },
button: { query: "[data-count-button]" },
},
methods: {
increment() {
this.state.count += 1;
},
},
render: {
value() {
return {
text: String(this.state.count),
};
},
button() {
return {
events: {
click: () => this.methods.increment(),
},
};
},
},
});
const root = document.querySelector("[data-counter]");
if (root) {
Counter.mount(root);
}

When mount() runs, Ornata:

  • resolves the root element
  • reads and prepares state
  • resolves configured elements
  • binds methods to the internal component instance
  • runs the mount lifecycle hook if present
  • performs an initial render pass for each state property

That state can come from component defaults, HTML data-* attributes on the root element, or an initialState object passed to mount().

Together, those pieces form a small component contract: the HTML stays visible, the state has a clear home, and the enhancement logic remains reusable.