Include Card
This card is rendered from an included partial.
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
| Directive | Best Use | Output Behavior |
|---|---|---|
@@include | Reusable partial with per-call data, or one selected item from a collection via from + select | Emits every time it appears, with optional compiler-resolved current / previous / next context |
@@include_once | One-time block in a source file | Emits only once per source file/path key |
@@loop | Repeated rows/items from arrays, JSON files, or filesystem sources | Emits one partial instance per resolved item |
Render a reusable partial with per-include data.
<!-- basic include -->
@@include('_html/component/example/file-include/demo-card.html', {
"title": "Include Card",
"description": "This card is rendered from an included partial."
})in/_html/component/example/file-include/demo-card.html <article class="demo-card">
<h3>@@title</h3>
<p>@@description</p>
</article>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.
This block should appear only once.
<!-- 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')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>Render repeating rows from an inline array payload.
<!-- 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>in/_html/component/example/file-include/demo-list-item.html <li><strong>@@name</strong> <span>- @@note</span></li>Load loop data from a JSON file path.
<ul>
@@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json')
</ul>in/_html/component/example/file-include/demo-list-item.html <li><strong>@@name</strong> <span>- @@note</span></li>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 }
]item_max Use loop.item_max in the third argument to cap emitted rows at compile time.
<ul>
@@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
"loop": {
"item_max": 2
}
})
</ul>in/_html/component/example/file-include/demo-list-item.html <li><strong>@@name</strong> <span>- @@note</span></li>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 }
]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.
category = news) <ul>
@@loop('_html/component/example/file-include/demo-list-item.html', 'example/data/file-include-posts.json', {
"loop": {
"filter": { "category": "news" }
}
})
</ul>featured === true + item_max) <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>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 }
]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.
item_max: 2 and category_filter: news) item_max, same filter) <!-- 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"
})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>@@if / @@elseif / @@else Branch chains select the first matching block in order. Use this when multiple outcomes share one decision path.
tier = pro) Tier: Pro
tier = basic) Tier: Basic
tier = starter, falls back to else) Tier: Free
<!-- 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"
})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>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 because context.showSummary is true.
<!-- 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" }
]
})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 inside @@for output -->
<li>@@context.links[i].label</li>
<!-- right inside @@for output -->
<li>`+context.links[i].label+`</li>in/_html/component/example/file-include/demo-context-control-flow.html This file is the same partial used in the rendered demo above.
@@include_once de-dupe is scoped to a source file, so the same include can still emit on a different page file.@@include when you need one selected item; use @@loop when you actually want repeated output.@@if / @@for control-flow expressions should read from context.*.@@for output, use backtick interpolation (for example `+context.links[i].label+`) for item values.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.
/example/article-one
Zero-based index 0 of 3
Previous: None
Next: Article Two
/example/article-two
Zero-based index 1 of 3
Previous: Article One
Next: Article Three
/example/article-three
Zero-based index 2 of 3
Previous: Article Two
Next: None
@@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" }
])_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 itemin/_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>@@loop can now enumerate directories directly from the project input tree by passing a source object instead of an array or JSON file path.
dirasset/font-icon/aidirasset/font-icon/browserdirasset/font-icon/example-calendardirasset/font-icon/example-debug-consoledirasset/font-icon/example-font-icon<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>Directory loop entries expose values such as name, type, path_from_project, path_web, plus loop metadata like _index, _length, _previous, and _next.
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>Use source.type = files with an optional glob-style match to enumerate project files.
file_html/component/example/file-include/demo-card.htmlfile_html/component/example/file-include/demo-context-control-flow.htmlfile_html/component/example/file-include/demo-fs-entry.htmlfile_html/component/example/file-include/demo-if-elseif-else.htmlfile_html/component/example/file-include/demo-include-select-neighbors.html<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>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>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.
/example/article-two
Resolved item 1 of 3
Previous: Article One
Next: Article Three
@@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"
}
})current: the matched item, or null if nothing matchedprevious: previous filtered item when neighbors is enablednext: 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 filteringin/_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>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" }
]in/_html/component/example/file-include/demo-card.htmlin/_html/component/example/file-include/demo-include-select-neighbors.htmlin/_html/component/example/file-include/demo-fs-entry.htmlin/_html/component/example/file-include/include-once-block.htmlin/_html/component/example/file-include/demo-list-item.htmlin/_html/component/example/file-include/demo-loop-neighbors.htmlin/_html/component/example/file-include/demo-post-loop.htmlin/_html/component/example/file-include/demo-context-control-flow.htmlin/_html/component/example/file-include/demo-if-elseif-else.htmlin/example/data/file-include-articles.jsonin/example/data/file-include-posts.jsonCurrent url matchesexample; excluded by this project'soption.package.ignore. See option.package.ignore docs .