All skills
michtio avatar

/craft-plugins

@44a2800

Index and router for plugin-specific Craft CMS 5 guidance — configuration, Twig API, PHP API, migrations, deployment, and pitfalls for the plugins this pack documents. Triggers whenever a task names one of these plugins in ANY context (build, configure, style, render, query, import, migrate, deploy, cache, debug): Formie (forms, submissions, File Upload, form in a migration, notifications, translations), SEOmatic (meta, sitemaps, JSON-LD, SEO field), Blitz (static/page caching, purge), Feed Me (XML/JSON/CSV import), Imager-X (transforms, srcset, quick syntax, named transforms, Power Pack, pppicture, ppimg), ImageOptimize (OptimizedImages), CKEditor (rich text, nested entries), Sprig (reactive, htmx), Element API (JSON endpoints), Retour (redirects, 404s), Navigation (nav menus), Hyper (link field), Colour Swatches, Password Policy (HIBP), Typogrify, Cache Igniter, Knock Knock (staging password), Elements Panel (N+1 debug), Sherlock (security scan), Amazon SES (SES/SNS bounce), Embedded Assets (oEmbed), Timeloop (recurring dates), Vite (craft.vite.*, asset bundling), Warp (passwordless login, magic link, one-time code/OTP, passkeys, WebAuthn, craft.warp, member sessions). Also load for passwordless or magic-link auth with NO plugin named. Always load when a task names one of these plugins — read references/<plugin>.md first. Do NOT trigger for Craft core with no plugin named (craftcms), template architecture (craft-site), or content modeling (craft-content-modeling).

Use this Skill: https://skilld.dev/gh/michtio/craftcms-claude-skills/craft-plugins

This session only. Nothing lands on disk.

referencesnavigation.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Navigation

Menu management plugin by Verbb. Create and manage navigation menus with drag-and-drop, nested structures, custom fields on nodes, multi-site support, and a rich Twig rendering API. The standard navigation solution for Craft CMS — 20,000+ active installs.

verbb/navigation — $19

Documentation

When unsure about a Navigation feature, WebFetch the docs.

Common Pitfalls

  • Querying nodes without .all() or .one() — craft.navigation.nodes() returns an element query, not results. Always call .all() to execute it.
  • Not using .level() for flat menus — without it, nested child nodes are included. Use .level(1) for top-level only.
  • Forgetting .eagerly() on node element fields — if nodes have custom relation fields, eager-load them to avoid N+1 queries.
  • Rendering with craft.navigation.render() then fighting the HTML — the render function outputs complete <ul><li> markup. If you need custom HTML, query nodes and build your own markup instead.
  • Not handling external URLs vs entry-linked nodes — some nodes link to entries (have an element), others are manual URLs. Template code must handle both.
  • Caching navigation without invalidation — navigation rarely changes, but when it does, cached markup is stale. Use {% cache %} with a key, or use Blitz which auto-invalidates.

Twig API

Quick Render (Default Markup)

{{ craft.navigation.render('mainNav') }}

Outputs a complete <nav><ul><li> structure with nested children. Good for prototyping, but you'll want custom rendering for production.

Render with Options

{{ craft.navigation.render('mainNav', {
    id: 'main-navigation',
    class: 'nav-list',
    ulClass: 'nav-list__items',
    liClass: 'nav-list__item',
    aClass: 'nav-list__link',
    activeClass: 'is-active',
    ulAttributes: { 'data-nav': 'main' },
}) }}

Custom Rendering (Recommended)

Query nodes and build your own markup for full control:

{% set nodes = craft.navigation.nodes()
    .handle('mainNav')
    .level(1)
    .all() %}

<nav aria-label="Main navigation">
    <ul class="flex gap-6">
        {% for node in nodes %}
            <li>
                <a href="{{ node.url }}"
                   class="nav-link {{ node.active ? 'is-active' : '' }}"
                   {{ node.newWindow ? 'target="_blank" rel="noopener noreferrer"' }}
                   {{ node.active ? 'aria-current="page"' }}>
                    {{ node.title }}
                </a>

                {# Nested children #}
                {% if node.hasDescendants %}
                    <ul class="submenu">
                        {% for child in node.children %}
                            <li>
                                <a href="{{ child.url }}"
                                   class="submenu-link {{ child.active ? 'is-active' : '' }}">
                                    {{ child.title }}
                                </a>
                            </li>
                        {% endfor %}
                    </ul>
                {% endif %}
            </li>
        {% endfor %}
    </ul>
</nav>

Node Properties

{{ node.title }}              {# Node label #}
{{ node.url }}                {# Full URL #}
{{ node.active }}             {# Boolean — is this node or a descendant active? #}
{{ node.isCurrent }}          {# Boolean — is this exact node active? #}
{{ node.newWindow }}          {# Boolean — opens in new tab? #}
{{ node.classes }}            {# Custom CSS classes #}
{{ node.customAttributes }}   {# Custom key/value attributes #}
{{ node.type }}               {# Node type (e.g., 'craft\elements\Entry') #}
{{ node.element }}            {# Linked Craft element (entry, category, etc.) or null #}
{{ node.hasDescendants }}     {# Boolean — has children? #}
{{ node.children }}           {# Child nodes array #}
{{ node.level }}              {# Nesting level (1 = top) #}
{{ node.parent }}             {# Parent node or null #}

Querying Nodes

{# All top-level nodes for a nav #}
{% set nodes = craft.navigation.nodes()
    .handle('mainNav')
    .level(1)
    .all() %}

{# Specific nav for a specific site #}
{% set nodes = craft.navigation.nodes()
    .handle('footerNav')
    .siteId(currentSite.id)
    .all() %}

{# With eager-loaded custom fields #}
{% set nodes = craft.navigation.nodes()
    .handle('mainNav')
    .with(['icon'])
    .all() %}

Active State Detection

Navigation automatically detects which node matches the current URL:

{% for node in nodes %}
    {# node.active — true if this node OR any descendant matches current URL #}
    {# node.isCurrent — true only if THIS node matches current URL #}
    <a href="{{ node.url }}"
       class="{{ node.active ? 'is-active' : '' }}"
       {{ node.isCurrent ? 'aria-current="page"' }}>
        {{ node.title }}
    </a>
{% endfor %}

Node Types

Type Description
Entry Links to a Craft entry (URL auto-syncs)
Category Links to a Craft category
Asset Links to a Craft asset
URL Manual URL (internal or external)
Custom Custom node type via plugin

Entry-linked nodes automatically update their URL when the entry's slug changes. Manual URL nodes are static.

Custom Fields on Nodes

Navigation nodes support custom field layouts. Add fields in CP → Navigation → Settings → Field Layout. Common additions:

  • Icon field — FontAwesome class or asset for nav icons
  • Subtitle field — secondary text for mega-menu items
  • Image field — thumbnails for visual navigation

Access in templates:

{% if node.icon %}
    <i class="{{ node.icon }}"></i>
{% endif %}
{{ node.title }}
{% if node.subtitle %}
    <span class="text-sm text-gray-500">{{ node.subtitle }}</span>
{% endif %}

Console Commands

# Resave all navigation nodes
ddev craft resave/navigation-nodes

GraphQL

{
  navigationNodes(navHandle: "mainNav", level: 1) {
    title
    url
    level
    newWindow
    classes
    children {
      title
      url
    }
  }
}

Pair With

  • Blitz — cache navigation globally. Blitz auto-invalidates when nav nodes change.
  • Hyper — for in-content links alongside structural navigation.

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The 'craft-plugins' skill is a technical reference and router for Craft CMS 5 plugins. It provides guidance on configuration, Twig and PHP APIs, and best practices for popular extensions in the Craft ecosystem. The skill consists of documentation files that describe legitimate plugin behaviors and does not contain any malicious code, obfuscation, or security vulnerabilities.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 44a2800. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated last month

README badge

README badge for michtio/craftcms-claude-skills/craft-plugins