# How Loops Iterate Over Data Sources with Variant Support in Instatic

> Learn how Instatic loops iterate over data sources using variant support and a round-robin algorithm. Discover immutable contexts for efficient data processing.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-07-26

---

**Instatic loops iterate over data sources using a round-robin variant selection algorithm that cycles through child template nodes for each item while maintaining immutable per-iteration contexts to prevent state pollution.**

In the CoreBunch/Instatic static site generator, **loops iterate over data sources with variant support** through a deterministic rendering pipeline implemented in [`src/core/publisher/renderLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderLoop.ts). This architecture enables dynamic content repetition with alternating layouts while supporting both static generation at build-time and interactive infinite pagination at runtime.

## Data Source Resolution and Fallback Behavior

When the publisher encounters a `base.loop` node, it first resolves the associated dataset through `config.loopData?.get(loopId)` as implemented at lines 60-66 of the renderer. This fetches the pre-computed list of items from registered providers such as collections, GraphQL queries, or custom `LoopItem` implementations.

If the data source is unavailable—common in editor preview environments—the system does not silently drop the layout. Instead, it emits an HTML comment to preserve the document structure and aid debugging. This ensures that empty states are visible during development rather than causing layout shifts or broken nesting.

## Variant Template System and Selection Logic

The loop node’s children function as **variant templates** stored in a `variants` array. The renderer validates that at least one child exists (lines 68-71), commenting-out the loop if no templates are registered.

For each data item at index `i`, the renderer selects the template using modular arithmetic:

```typescript
// From src/core/publisher/renderLoop.ts (lines 84-85)
const variantIndex = i % variants.length;
const selectedVariant = variants[variantIndex];

```

This **round-robin selection** means that with *N* child templates, the layout cycles through all *N* variants sequentially as items are rendered. For example, with two card variants, odd items render with the first template while even items use the second, creating visual rhythm without manual conditional logic.

## Immutable Per-Iteration Context Creation

To support nested loops and component references without side effects, the system constructs an immutable context for every iteration. The implementation creates a fresh `entryStack` array using the spread operator:

```typescript
// From src/core/publisher/renderLoop.ts (lines 89-96)
const entryStack = [...baseStack, item];
const context = new TemplateRenderDataContext(entryStack);
const iterationConfig = new RenderConfig({
  ...config,
  templateContext: context
});

```

This pattern ensures that each item receives a stable, item-specific `RenderConfig` instance. Nested visual components or inner loops reference this isolated context without mutating the outer configuration, preventing **context pollution** between sibling iterations.

## Rendering Execution and Pagination Support

With the variant selected and context isolated, the publisher invokes the `renderNode` function (lines 97-98) with the chosen variant ID and the per-iteration configuration. This produces the HTML for that specific data item using the appropriate template.

When the loop’s `pagination` property is set to `'infinite'`, the renderer adds sentinel data attributes to enable client-side hydration:

```typescript
// From src/core/publisher/renderLoop.ts (lines 100-112)
attributes['data-instatic-loop'] = loopId;
attributes['data-instatic-loop-page'] = currentPage;
// Additional registration for client-side fetching...

```

These attributes allow the runtime JavaScript to identify paginated boundaries and fetch additional pages on demand, bridging static generation with dynamic user interactions.

## HTML Wrapper Generation and Style Injection

The final output wraps the accumulated iteration HTML in a configurable container tag. The `resolveHtmlTag` helper (located in [`src/modules/base/utils/htmlTag.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/utils/htmlTag.ts)) safely selects the wrapper element, defaulting to a `div` when no specific tag is configured.

The system then injects the node’s `classIds` and inline styles through [`src/core/publisher/classInjection.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/classInjection.ts) (lines 14-23 and 20-27), ensuring that loop-level styling applies to the wrapper while individual variant styles handle item-specific presentation.

## Practical Implementation Example

The following configuration demonstrates a blog post list with alternating card layouts:

```tsx
// Loop configuration with two variant templates
{
  "type": "base.loop",
  "id": "posts-loop",
  "props": { "pagination": "none" },
  "children": [
    { "type": "base.card", "id": "card-variant-1", "props": { "style": "compact" } },
    { "type": "base.card", "id": "card-variant-2", "props": { "style": "spacious" } }
  ]
}

// Server-side data registration
import { registerLoopData } from '@core/loops/runtime';

registerLoopData('posts-loop', {
  items: [
    { title: 'First Post', url: '/post/1' },
    { title: 'Second Post', url: '/post/2' },
    { title: 'Third Post', url: '/post/3' }
  ],
  pageNumber: 1,
  hasMore: false
});

```

When published, the first post renders with `card-variant-1`, the second with `card-variant-2`, and the third cycles back to `card-variant-1`, demonstrating the round-robin behavior.

## Summary

- **Data source resolution** occurs via `config.loopData?.get(loopId)` with HTML comment fallbacks for missing data.
- **Round-robin variant selection** uses `i % variants.length` to cycle through child templates deterministically.
- **Immutable context isolation** prevents state pollution through spread-operator snapshots and fresh `RenderConfig` instances per iteration.
- **Infinite pagination** injects `data-instatic-loop` attributes for client-side runtime extension.
- **HTML wrapping** uses `resolveHtmlTag` and class injection utilities to finalize the output structure.

## Frequently Asked Questions

### How does Instatic handle missing loop data during rendering?

When `config.loopData?.get(loopId)` returns undefined, typically in editor preview environments, the renderer emits an HTML comment instead of silently omitting the layout. This preserves document structure and provides visual feedback during development.

### What algorithm determines which variant template renders for each loop item?

Instatic uses round-robin selection via modular arithmetic `i % variants.length`, where `i` is the zero-based item index. This cycles sequentially through available child templates, ensuring even distribution of layouts across the dataset.

### How does Instatic prevent context pollution between nested loops?

Each iteration creates an immutable snapshot using `[...baseStack, item]` wrapped in a new `TemplateRenderDataContext` and `RenderConfig`. This isolation ensures that nested loops or component references cannot mutate the parent loop’s state.

### What attributes support infinite pagination in Instatic?

When `pagination` is set to `'infinite'`, the renderer injects `data-instatic-loop`, `data-instatic-loop-page`, and related sentinel attributes while registering the loop ID. These markers enable client-side JavaScript to fetch and append additional pages dynamically.