Skip to content

List Rendering

This example shows how index helps when repeated HTML elements share one piece of state but still need individual DOM updates.

In this example, a group of related elements is retrieved from the DOM, but each one needs to respond differently to the component’s current state. For example, only one step can be active at a time, while the others may be complete or still upcoming. The framework-provided index value in the render context makes it possible to identify which element in the list is being rendered and apply the correct output to each one.

Open this demo in a new tab

<section data-stepper>
<ol>
<li data-step>Step 1</li>
<li data-step>Step 2</li>
<li data-step>Step 3</li>
</ol>
<div>
<button data-prev>Previous</button>
<button data-next>Next</button>
</div>
</section>
import { defineComponent } from 'ornata';
export const Stepper = defineComponent({
name: 'Stepper',
state: {
activeIndex: { default: 0, type: Number },
},
elements: {
steps: { queryAll: '[data-step]', min: 2 },
prev: { query: '[data-prev]' },
next: { query: '[data-next]' },
},
methods: {
previous() {
this.state.activeIndex = Math.max(0, this.state.activeIndex - 1);
},
next() {
this.state.activeIndex = Math.min(
this.elements.steps.length - 1,
this.state.activeIndex + 1
);
},
},
render: {
// 👇 Use the element's index in the list to decide how it should render.
steps({ index }) {
const currentIndex = index ?? 0;
const isActive = currentIndex === this.state.activeIndex;
const isComplete = currentIndex < this.state.activeIndex;
return {
attributes: {
'aria-current': isActive ? 'step' : null,
},
classes: {
'is-active': isActive,
'is-complete': isComplete,
},
};
},
prev() {
return {
attributes: {
type: 'button',
disabled: this.state.activeIndex === 0,
},
events: {
click: () => this.methods.previous(),
},
};
},
next() {
return {
attributes: {
type: 'button',
disabled:
this.state.activeIndex ===
this.elements.steps.length - 1,
},
events: {
click: () => this.methods.next(),
},
};
},
},
});

Without index, each repeated element would need more custom plumbing.

With it, each step can respond to shared component state while still rendering individually as active, complete, or upcoming.