nette/claude-code/latte-templates

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Invoke before creating or modifying .latte files, even for single-line changes. Provides Latte syntax, tags, filters, n:attributes, layouts, template inheritance, AJAX snippets, extensions, and Nette integration. Also trigger when user mentions Latte by name.

Install

npx vskill@latest install nette/claude-code/latte-templates

SKILL.md

Latte Templating System

Latte is a secure templating engine with context-aware escaping, intuitive syntax, and powerful template inheritance.

shell
composer require latte/latte
Basic Syntax
latte
{* this is a comment *}
<ul n:if="$items"> {* n:attribute *}
{foreach $items as $item} {* tag *}
<li>{$item|capitalize}</li> {* variable with filter *}
{/foreach}
</ul>
Printing Variables
latte
{$name} {* prints escaped variable *}
{$user->name} {* object property *}
{$items[0]} {* array access *}
{='hello'|upper} {* expression with filter *}
{$html|noescape} {* disable escaping (use carefully!) *}

Latte uses context-aware escaping — output adapts to where the variable is printed. In <script> context, arrays and objects are automatically serialized to JSON:

latte
{* Arrays auto-serialize to JSON inside <script> *}
<script type="application/ld+json">{$schemaData}</script>
<script>
let config = {$config};
</script>

Never use json_encode() in PHP + |noescape for this — Latte handles serialization and escaping safely on its own.

Filters

Filters modify output, written after |:

latte
{$title|upper} {* HELLO *}
{$text|truncate:100} {* shortens to 100 chars *}
{$price|number:2} {* formats number *}
{$date|date:'j. n. Y'} {* formats date *}
{$name|lower|capitalize} {* chained filters *}

Common filters: upper, lower, capitalize, truncate, number, date, noescape, escapeUrl, stripHtml, trim, replace, first, last, length, sort, reverse

The filter | applies to the entire expression before it. Parentheses around PHP operators (like ??) before a filter are unnecessary:

latte
{$a ?? $b|texyLine|noescape} {* correct – filter applies to result of ?? *}
{($a ?? $b)|texyLine|noescape} {* redundant – parentheses change nothing *}

See the complete filter reference for all available filters.

n:attributes

Pair tags can be written as HTML attributes:

latte
{* These are equivalent: *}
{if $condition}<div>...</div>{/if}
<div n:if="$condition">...</div>
{* Applies to element content only: *}
<div n:inner-foreach="$items as $item">...</div>
{* Applies to tag only (not content): *}
<a href={$url} n:tag-if="$url">Link</a>
Conditions and Loops

{if} / {elseif} / {else}, {foreach} and {for} work as expected. What is Latte-specific:

latte
{ifset $user}...{/ifset} {* the variable exists, unlike {if isset()} *}
{switch $type} {* no break, cases do not fall through *}
{case admin}Administrator
{case user}User
{default}Guest
{/switch}
{foreach $items as $item}
<li>{$item}</li>
{else} {* runs when the array is empty *}
<li>No items found</li>
{/foreach}
{foreach $items as $item}
{$iterator->counter}. {$item} {* 1, 2, 3…; also ->first, ->last, ->odd, ->even *}
{first}<ul>{/first} {* rendered only in the first iteration *}
{last}</ul>{/last}
{sep}, {/sep} {* separator, skipped after the last item *}
{/foreach}
Variables
latte
{var $name = 'John'}
{var $items = [1, 2, 3]}
{default $lang = 'en'} {* only if not set *}
{capture $content}
<p>Captured HTML</p>
{/capture}
{$content}
Template Inheritance

Layout template (`@layout.latte`):

latte
<!DOCTYPE html>
<html>
<head>
<title>{block title}Default{/block}</title>
</head>
<body>
{block content}{/block}
</body>
</html>

Child template:

latte
{layout '@layout.latte'}
{block title}My Page{/block}
{block content}
<h1>Welcome</h1>
<p>Content here</p>
{/block}
Including Templates
latte
{include 'header.latte'}
{include 'item.latte', item: $item, showPrice: true}
{include $dynamicTemplate}
Blocks
latte
{block sidebar}
<aside>Sidebar content</aside>
{/block}
{include sidebar} {* print block *}
{include sidebar from 'other.latte'} {* from another file *}
{* Reusable definitions with parameters *}
{define button, $text, $type = 'primary'}
<button class="btn btn-{$type}">{$text}</button>
{/define}
{include button, 'Submit'}
{include button, 'Cancel', 'secondary'}
Other Common Tags
TagDescription
{do expression}Execute without output
{php expression}Obsolete alias for {do} – a single expression only. Real PHP code needs RawPhpExtension
{dump $var}Debug dump (Tracy)

See the complete tag reference for all available tags.

Smart HTML Attributes (Latte 3.1)

Applies only when the whole attribute value is one {...} expression (title={$t} or title="{$t}"). In a mixed value (title="foo {$t}") none of it applies – null becomes '', an array becomes "Array". n:attr follows the same rules, so {if} / n:attr wrappers around attributes are obsolete.

KindAttributesValue handling
boolallowfullscreen async autofocus autoplay checked controls default defer disabled formnovalidate inert ismap itemscope loop multiple muted nomodule novalidate open playsinline readonly required reversed selectedtruthy → bare name; falsey (false, null, 0, '', []) → omitted
tristatecontenteditable draggable spellcheck writingsuggestionsbool → the keywords "true" / "false"; null omits the attribute
valued boolhidden popoverflag like a bool, but a non-empty value survives (hidden="until-found"); falsy is tested first, so '0' omits it rather than rendering an invalid value
listaccesskey class headers itemprop ping rel role sandboxarray → space-separated
stylestylearray → prop: value; …
datadata-*array / stdClass → JSON; bool → "true" / "false"
ariaaria-*bool → "true" / "false"; array → space-separated
plaineverything elsestring/int/float/Stringable printed; null omits the attribute; array, bool or other object → E_USER_WARNING and the attribute is dropped

In list/aria arrays a plain item is used as-is, a keyed item yields the key only if its value is === true (active => 1 renders 1, not active); items with null, false, 0, '' are skipped, an all-skipped array omits the attribute. In style, a keyed item is skipped by the same rule, so opacity => 0 vanishes ('0px' survives).

latte
<input type="text" disabled={$disabled} readonly={$readonly}>
{* false, true → <input type="text" readonly> *}
<div title={$title}>x</div> {* null → <div>x</div> *}
<div title="Hi {$name}">x</div> {* null → <div title="Hi ">x</div> (mixed value) *}
<div title={$title|upper}>x</div> {* null → <div title="">x</div> (filter stringifies null) *}
<div title={$title?|upper}>x</div> {* null → <div>x</div> (?| preserves the drop) *}
<button class={[btn, btn-primary, active => $isActive, disabled => $isDisabled]}>x</button>
{* true, false → <button class="btn btn-primary active">x</button> *}
<a rel={[nofollow, external => $isExternal]}>l</a> {* true → <a rel="nofollow external">l</a> *}
<span title={[a, b]}></span> {* warning: array is not allowed for 'title'; attribute dropped *}
<input value={$rows}> {* array → same warning, no more value="Array" *}
<div style={[background => lightblue, opacity => 0, display => $vis ? block : null]}></div>
{* false → <div style="background: lightblue"></div> *}
<div data-config={[theme: dark, version: 2]} data-open={=true}></div>
{* → <div data-config='{"theme":"dark","version":2}' data-open="true"></div> *}
<button aria-expanded={=false} aria-labelledby={[title, desc => $has]}></button>
{* false → <button aria-expanded="false" aria-labelledby="title"></button> *}
<div data-active={$on|toggle}></div> {* toggle = bool handling for any attribute; false → <div></div> *}
<div title={$cfg|json}></div> {* json (must be the last filter) = JSON handling *}

A real filter defeats smart handling – it stringifies the value: class={$arr|noescape} renders class="Array". Keep smart attributes filter-free; toggle, json, nocheck and accept are compile-time hints, not filters, and are safe.

Traps:

  • URL attributes are exempt (href, src, action, formaction, <object data>): Latte

appends an internal checkUrl filter, so href={$url} with null renders href="", and an array gives href="Array" with only a PHP notice. Use href={$url?|nocheck} for the drop (this also skips the URL safety check).

  • Tristate attributes distinguish three states: true and false render the keyword,

null drops the attribute. That matters, because "missing" and ="false" mean different things — contenteditable="false" disables editing inside an editable region, whereas a missing attribute inherits from the parent.

Prefer class={[...]} and smart attributes over n:class / n:attr

Since 3.1 the attribute forms cover almost everything the n: ones did, so new templates should reach for them first. Neither n:class nor n:attr is deprecated in Latte, and there is one case only n:attr can express (below), so this is a preference, not a removal.

Do not rewrite existing templates mechanically — two differences change the rendered output:

latte
<a n:class="$cnt ? open, item"> {* → *} <a class={[$cnt ? open, item]}> {* keep the ternary *}
<a class={[open => $cnt]}> {* wrong – $cnt = 3 renders class="3" *}
<a class={[sel => $row]}> {* wrong – a Stringable renders its string, a plain object throws *}
<option n:attr="selected: $sel"> {* → *} <option selected={$sel}>
  1. A keyed item applies only when its value is `=== true`. With any other truthy value the

value is printed instead of the key, which is silent. Keep the ternary rather than moving the condition into a key.

  1. `n:class` de-duplicates the list (array_unique), class={[...]} does not — the same

class listed twice survives after the rewrite.

n:class additionally rejects an array variable (n:class="$arr" → class="Array"; use class={$arr}) and a sibling class="…" on the same tag (CompileException); both fail loudly.

The one case n:attr still owns is a dynamic set or name of attributes, which no static attribute can express:

latte
<tr n:attr="$attrs"> {* ['data-id' => 5, 'class' => 'row'] → data-id="5" class="row" *}
<a n:attr="[$name => $val]"> {* attribute name from a variable *}

Migration from 3.0 – changed: null used to print =""; data-/aria- bools printed "1"/""; data- arrays printed a space-separated list; on* values were JSON-encoded. $engine->setFeature(Latte\Feature::MigrationWarnings, true) warns on every affected attribute; the |accept filter silences it per value.


Latte in Nette Applications

Template Organization Strategy

Keep templates with presenters:

Product/
├── ProductPresenter.php
├── default.latte
├── edit.latte
└── detail.latte

Layout placement follows presenter organization:

Admin/
├── @layout.latte ← Admin-wide layout
├── Auth/
│ ├── @layout.latte ← Auth-specific layout
│ └── AuthPresenter.php
└── Catalog/
└── Product/
├── ProductPresenter.php
└── edit.latte

The more specific a layout, the closer it sits to the presenters it serves; whether it extends the enclosing one is up to it. Keep such chains short: a part that only some pages share usually fits better through {include} or {define} than through another layout level.

Template Partial Patterns

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
37
Repository forks
4
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 18 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/nette/claude-code/latte-templates/badge)](https://verified-skill.com/skills/nette/claude-code/latte-templates)

Something wrong with this skill or its page? Tell us.