Skip to main
Table of Contents

File Include

This page demonstrates practical usage of @@include, @@include_once, and @@loop using real partials. Each demo shows rendered output and the exact source snippet used to produce it.

The current include system supports both classic reusable partials and collection-aware single-item resolution. That means a detail page can now ask the compiler for one matching item plus its neighbors without looping an entire dataset just to suppress most renders.

Synticore uses its own fork of gulp-file-include called cure-gulp-file-include. It was created to add item_max and filter loop controls, collection-aware @@include selection, plus @@elseif / @@else conditional-chain support.

These directives are compile-time features. They do not execute in the browser at runtime.

Learn more in depth in the Synticore Wiki: File Include Guide

When To Use Which Directive

DirectiveBest UseOutput Behavior
@@includeReusable partial with per-call data, or one selected item from a collection via from + selectEmits every time it appears, with optional compiler-resolved current / previous / next context
@@include_onceOne-time block in a source fileEmits only once per source file/path key
@@loopRepeated rows/items from arrays, JSON files, or filesystem sourcesEmits one partial instance per resolved item

1) Basic Include

Render a reusable partial with per-include data.

Rendered

Include Card

This card is rendered from an included partial.

Code

<!-- basic include -->
@@include('_html/component/example/file-include/demo-card.html', {
  "title": "Include Card",
  "description": "This card is rendered from an included partial."
})

File: in/_html/component/example/file-include/demo-card.html

<article class="demo-card">
  <h3>@@title</h3>
  <p>@@description</p>
</article>

2) Include Once

Include the same file twice, but emit it only once. In this module, de-dupe is scoped per source file and keyed by the parsed include path string.

Rendered

This block should appear only once.

Code

<!-- include_once emits once per source file -->
@@include_once('_html/component/example/file-include/include-once-block.html')
@@include_once('_html/component/example/file-include/include-once-block.html')

File: in/_html/component/example/file-include/include-once-block.html

<div class="include-once-demo" id="include-once-demo-root">
  <p>This block should appear only once.</p>
</div>

3) Loop With Inline Array

Render repeating rows from an inline array payload.

Rendered

  • Alpha - Inline row one - Category "@@category" - Featured "False"
  • Beta - Inline row two - Category "@@category" - Featured "False"
  • Gamma - Inline row three - Category "@@category" - Featured "False"

Code

<!-- loop with inline array -->
<ul>
@@loop('_html/component/example/file-include/demo-list-item.html', [
  { "name": "Alpha", "note": "Inline row one" },
  { "name": "Beta", "note": "Inline row two" },
  { "name": "Gamma", "note": "Inline row three" }
])
</ul>

File: in/_html/component/example/file-include/demo-list-item.html

<li><strong>@@name</strong> <span>- @@note</span></li>

4) Loop From JSON File

Load loop data from a JSON file path.

Rendered

  • Post One - JSON row one - Category "news" - Featured "True"
  • Post Two - JSON row two - Category "updates" - Featured "False"
  • Post Three - JSON row three - Category "news" - Featured "False"
  • Post Four - JSON row four - Category "guides" - Featured "True"

Code

<ul>
  @@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json')
</ul>

File: in/_html/component/example/file-include/demo-list-item.html

<li><strong>@@name</strong> <span>- @@note</span></li>

File: in/example/data/file-include-posts.json

[
  { "name": "Post One", "note": "JSON row one", "category": "news", "featured": true },
  { "name": "Post Two", "note": "JSON row two", "category": "updates", "featured": false },
  { "name": "Post Three", "note": "JSON row three", "category": "news", "featured": false },
  { "name": "Post Four", "note": "JSON row four", "category": "guides", "featured": true }
]

5) Loop With item_max

Use loop.item_max in the third argument to cap emitted rows at compile time.

Rendered

  • Post One - JSON row one - Category "news" - Featured "True"
  • Post Two - JSON row two - Category "updates" - Featured "False"

Code

<ul>
  @@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
    "loop": {
      "item_max": 2
    }
  })
</ul>

File: in/_html/component/example/file-include/demo-list-item.html

<li><strong>@@name</strong> <span>- @@note</span></li>

File: in/example/data/file-include-posts.json

[
  { "name": "Post One", "note": "JSON row one", "category": "news", "featured": true },
  { "name": "Post Two", "note": "JSON row two", "category": "updates", "featured": false },
  { "name": "Post Three", "note": "JSON row three", "category": "news", "featured": false },
  { "name": "Post Four", "note": "JSON row four", "category": "guides", "featured": true }
]

6) Loop With filter

Use loop.filter in loop options to select rows from a shared data file before rendering. This keeps one source list reusable across multiple sections/pages.

Rendered (Object Filter: category = news)

  • Post One - JSON row one - Category "news" - Featured "True"
  • Post Three - JSON row three - Category "news" - Featured "False"

Code

<ul>
  @@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
    "loop": {
      "filter": { "category": "news" }
    }
  })
</ul>
  • Post One - JSON row one - Category "news" - Featured "True"

Code

<ul>
  @@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
    "loop": {
      "filter": "item.featured === true",
      "item_max": 1
    }
  })
</ul>

File: in/example/data/file-include-posts.json

[
  { "name": "Post One", "note": "JSON row one", "category": "news", "featured": true },
  { "name": "Post Two", "note": "JSON row two", "category": "updates", "featured": false },
  { "name": "Post Three", "note": "JSON row three", "category": "news", "featured": false },
  { "name": "Post Four", "note": "JSON row four", "category": "guides", "featured": true }
]

7) Single Inline: Optional item_max + Context Filter

Keep one @@loop inline and pass loop controls under loop plus shared values under context. This demo uses one reusable partial and shows calls with and without item_max.

Rendered (With item_max: 2 and category_filter: news)

  • Post One - JSON row one - Category "news" - Featured "True"
  • Post Three - JSON row three - Category "news" - Featured "False"

Rendered (No item_max, same filter)

  • Post One - JSON row one - Category "news" - Featured "True"
  • Post Three - JSON row three - Category "news" - Featured "False"

Code

<!-- reusable wrapper partial call -->
@@include('_html/component/example/file-include/demo-post-loop.html', {
  "item_max": 2,
  "category_filter": "news",
  "style_column": "example-style-value"
})

@@include('_html/component/example/file-include/demo-post-loop.html', {
  "category_filter": "news",
  "style_column": "example-style-value"
})

File: in/_html/component/example/file-include/demo-post-loop.html

<ul>
@@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
  "loop": {
    "item_max": "@@item_max",
    "filter": "!context.category_filter || context.category_filter === 'all' || item.category === context.category_filter"
  },
  "context": {
    "style_column": "@@style_column"
  }
})
</ul>

8) Conditional Chain With @@if / @@elseif / @@else

Branch chains select the first matching block in order. Use this when multiple outcomes share one decision path.

Rendered (tier = pro)

Tier: Pro

Rendered (tier = basic)

Tier: Basic

Rendered (tier = starter, falls back to else)

Tier: Free

Code

<!-- conditional branch chain -->
@@include('_html/component/example/file-include/demo-if-elseif-else.html', {
  "tier": "pro"
})

@@include('_html/component/example/file-include/demo-if-elseif-else.html', {
  "tier": "basic"
})

@@include('_html/component/example/file-include/demo-if-elseif-else.html', {
  "tier": "starter"
})

File: in/_html/component/example/file-include/demo-if-elseif-else.html

<div class="demo-branch-row">
@@if (context.tier === 'pro') {
  <p><strong>Tier:</strong> Pro</p>
}
@@elseif (context.tier === 'basic') {
  <p><strong>Tier:</strong> Basic</p>
}
@@else {
  <p><strong>Tier:</strong> Free</p>
}
</div>

9) Control Flow With context.* and Backtick Output

@@if and @@for expressions evaluate against a context object. In cure-gulp-file-include, the most reliable way to emit loop-item values in markup is backtick interpolation with inline expressions.

Rendered

Rendered because context.showSummary is true.

  • First
  • Second
  • Third

Code (Caller)

<!-- context payload passed to the partial -->
@@include('_html/component/example/file-include/demo-context-control-flow.html', {
  "showSummary": true,
  "summary": "Rendered because context.showSummary is true.",
  "links": [
    { "label": "First" },
    { "label": "Second" },
    { "label": "Third" }
  ]
})

Code (Partial)

Use context.* in conditions and loop bounds, then emit item values with backticks:

@@if (context.showSummary) {
  <p>@@summary</p>
}
<ul>
@@for (var i = 0; i < context.links.length; i++) {
  <li>`+context.links[i].label+`</li>
}
</ul>

Wrong vs Right

<!-- wrong inside @@for output -->
<li>@@context.links[i].label</li>

<!-- right inside @@for output -->
<li>`+context.links[i].label+`</li>

File: in/_html/component/example/file-include/demo-context-control-flow.html

This file is the same partial used in the rendered demo above.

Common Pitfalls

  • Relative include paths are resolved from the source file doing the include, not from the output path.
  • @@include_once de-dupe is scoped to a source file, so the same include can still emit on a different page file.
  • Use collection-aware @@include when you need one selected item; use @@loop when you actually want repeated output.
  • @@if / @@for control-flow expressions should read from context.*.
  • Inside @@for output, use backtick interpolation (for example `+context.links[i].label+`) for item values.
  • Filesystem loop sources enumerate compile-time project files/directories, not browser-visible runtime state.

10) Loop Neighbor Metadata

Each @@loop item now exposes adjacent item objects as _previous and _next. This is useful for previous/next article navigation when a page is generated from an ordered list.

Rendered

Article One

/example/article-one

Zero-based index 0 of 3

Previous: None
Next: Article Two

Article Three

/example/article-three

Zero-based index 2 of 3

Previous: Article Two
Next: None

Code

@@loop('_html/component/example/file-include/demo-loop-neighbors.html', [
  { "title": "Article One", "link": "/example/article-one" },
  { "title": "Article Two", "link": "/example/article-two" },
  { "title": "Article Three", "link": "/example/article-three" }
])

Metadata Reference

  • _key: current item key as a string
  • _index: zero-based index
  • _first: true for the first item
  • _last: true for the last item
  • _length: total item count after filtering
  • _previous: previous item object, or null for the first item
  • _next: next item object, or null for the last item

File: in/_html/component/example/file-include/demo-loop-neighbors.html

<article class="demo-card">
  <h3>@@title</h3>
  <p><code>@@link</code></p>
  <p>Zero-based index <strong>@@_index</strong> of <strong>@@_length</strong></p>
  <p>
    Previous:
    @@if (_previous) {
      <a href="@@_previous.link">@@_previous.title</a>
    }
    @@else {
      <span>None</span>
    }
    <br>
    Next:
    @@if (_next) {
      <a href="@@_next.link">@@_next.title</a>
    }
    @@else {
      <span>None</span>
    }
  </p>
</article>

11) Loop From Source Directories

@@loop can now enumerate directories directly from the project input tree by passing a source object instead of an array or JSON file path.

Rendered

  • ai - dir
    asset/font-icon/ai
  • browser - dir
    asset/font-icon/browser
  • example-calendar - dir
    asset/font-icon/example-calendar
  • example-debug-console - dir
    asset/font-icon/example-debug-console
  • example-font-icon - dir
    asset/font-icon/example-font-icon

Code

<ul>
  @@loop('_html/component/example/file-include/demo-fs-entry.html', {
    "source": {
      "type": "dirs",
      "dir": "asset/font-icon"
    }
  }, {
    "loop": {
      "filter": "['example', 'icon', 'interface', 'license', 'no-package', 'synticore'].indexOf(item.name) === -1",
      "item_max": 5
    }
  })
</ul>

Emitted Item Shape

Directory loop entries expose values such as name, type, path_from_project, path_web, plus loop metadata like _index, _length, _previous, and _next.

File: in/_html/component/example/file-include/demo-fs-entry.html

<li>
  <strong>@@name</strong>
  <span>- <code>@@type</code></span>
  <br>
  <small><code>@@path_from_project</code></small>
</li>

12) Loop From Source Files

Use source.type = files with an optional glob-style match to enumerate project files.

Rendered

  • demo-card.html - file
    _html/component/example/file-include/demo-card.html
  • demo-context-control-flow.html - file
    _html/component/example/file-include/demo-context-control-flow.html
  • demo-fs-entry.html - file
    _html/component/example/file-include/demo-fs-entry.html
  • demo-if-elseif-else.html - file
    _html/component/example/file-include/demo-if-elseif-else.html
  • demo-include-select-neighbors.html - file
    _html/component/example/file-include/demo-include-select-neighbors.html

Code

<ul>
  @@loop('_html/component/example/file-include/demo-fs-entry.html', {
    "source": {
      "type": "files",
      "dir": "_html/component/example/file-include",
      "match": "demo-*.html"
    }
  }, {
    "loop": {
      "item_max": 5
    }
  })
</ul>

File: in/_html/component/example/file-include/demo-fs-entry.html

<li>
  <strong>@@name</strong>
  <span>- <code>@@type</code></span>
  <br>
  <small><code>@@path_from_project</code></small>
</li>

13) Include With from + select

Use @@include with a collection source when one partial should render exactly one selected item. This keeps lookup and neighbor resolution in the compiler instead of forcing a full @@loop render pass.

The included partial stays focused on rendering because the compiler now hands it current, previous, and next directly.

Rendered

Code

@@include('_html/component/example/file-include/demo-include-select-neighbors.html', {
  "from": "example/data/file-include-articles.json",
  "filter": { "category": "reviews" },
  "select": "item.link === context.article_link",
  "neighbors": true,
  "context": {
    "article_link": "/example/article-two"
  }
})

Resolved Include Context

  • current: the matched item, or null if nothing matched
  • previous: previous filtered item when neighbors is enabled
  • next: next filtered item when neighbors is enabled
  • _index: zero-based index inside the filtered collection, or -1 if not found
  • _length: total item count after filtering

File: in/_html/component/example/file-include/demo-include-select-neighbors.html

<article class="demo-card">
  @@if (current) {
    <h3>@@current.title</h3>
    <p><code>@@current.link</code></p>
    <p>Resolved item <strong>@@_index</strong> of <strong>@@_length</strong></p>
    <p>
      Previous:
      @@if (previous) {
        <a href="@@previous.link">@@previous.title</a>
      }
      @@else {
        <span>None</span>
      }
      <br>
      Next:
      @@if (next) {
        <a href="@@next.link">@@next.title</a>
      }
      @@else {
        <span>None</span>
      }
    </p>
  }
  @@else {
    <p>No matching article was found.</p>
  }
</article>

File: in/example/data/file-include-articles.json

[
  { "title": "Article One", "link": "/example/article-one", "category": "reviews" },
  { "title": "Article Two", "link": "/example/article-two", "category": "reviews" },
  { "title": "Article Three", "link": "/example/article-three", "category": "reviews" },
  { "title": "Article Four", "link": "/example/article-four", "category": "news" }
]

Referenced Files

  • in/_html/component/example/file-include/demo-card.html
  • in/_html/component/example/file-include/demo-include-select-neighbors.html
  • in/_html/component/example/file-include/demo-fs-entry.html
  • in/_html/component/example/file-include/include-once-block.html
  • in/_html/component/example/file-include/demo-list-item.html
  • in/_html/component/example/file-include/demo-loop-neighbors.html
  • in/_html/component/example/file-include/demo-post-loop.html
  • in/_html/component/example/file-include/demo-context-control-flow.html
  • in/_html/component/example/file-include/demo-if-elseif-else.html
  • in/example/data/file-include-articles.json
  • in/example/data/file-include-posts.json