Skip to content
Development
Skill

/craftcms

Craft CMS 5 plugin and module development — extending Craft with PHP. Covers elements, element queries, services, models, records, controllers, migrations, queue jobs, console commands, field types, native fields, events, behaviors, Twig extensions, widgets, filesystems,

From plugin
craftcms-claude-skills
8914 skills6 agents

Claude Code

$ npx -y skills add michtio/craftcms-claude-skills --skill craftcms --agent claude-code

SKILL.md

craftcms.SKILL.md
name: craftcms
description: "Craft CMS 5 plugin and module development — extending Craft with PHP. Covers elements, element queries, services, models, records, controllers, migrations, queue jobs, console commands, field types, native fields, events, behaviors, Twig extensions, widgets, filesystems, permissions, project config, GraphQL, testing, and debugging."
when_to_use: "Triggers on: beforePrepare()/afterSave()/defineSources()/defineTableAttributes()/attributeHtml(), MemoizableArray, BaseNativeField, EVENT_REGISTER_*/DEFINE_*/BEFORE_*/AFTER_*, CraftVariable, custom element or field type (normalizeValue/serializeValue/inputHtml), webhook, API endpoint, queue/batch job, CP section, dashboard widget, utility page, element action/exporter/condition, registerUserPermissions, requirePermission vs requireAdmin, kebab-case permissions, allowAdminChanges, canView/canSave/canDelete, defineRules/EVENT_DEFINE_RULES on Entry (fieldId + ownerId must be set), elevated session, project-config/apply/write, drafts/revisions, edit sidebar (EVENT_DEFINE_SIDEBAR_HTML)/toolbar buttons, metaFieldsHtml, VueAdminTable, GeneralConfig, getIsMultiSite/refreshSites stale after new site, deleteSite phantom sites, siteId filter lost, UTC datetime/strtotime drift, ['like'] tuple matches nothing, configWarning. Always use when writing, editing, or reviewing Craft plugin/module PHP (even when no API is named) or saveField()/saveEntryType() from scripts. Plugin work: also load craft-plugins. Do NOT trigger for front-end Twig (craft-site) or content modeling (craft-content-modeling)."

Craft CMS 5 — Extending (Plugins & Modules)

Reference for extending Craft CMS 5 through plugins and modules. Covers everything from elements and services to controllers, migrations, fields, and events.

This skill is scoped to **extending** Craft — building plugins, modules, custom element types, field types, and backend integrations. For site/platform development (content modeling, sections, entry types, Twig templating, plugin selection), see the `craft-site` skill.

Companion Skills — Always Load Together

When this skill triggers, also load:

  • **`craft-php-guidelines`** — PHPDoc standards, section headers, naming conventions, class organization, ECS/PHPStan, verification checklist. Required for any PHP code.
  • **`ddev`** — All commands run through DDEV. Required for running ECS, PHPStan, scaffolding, and tests.
  • **`craft-garnish`** — When working on CP JavaScript, asset bundles, or interactive CP components. Covers Garnish's class system, UI widgets (Modal, HUD, DisclosureMenu, Select), drag system, and the Craft.* JS class pattern.
  • **`craft-pest`** — When writing, running, fixing, or reviewing tests. Covers the `markhuot/craft-pest-core` harness, database isolation (its rollback is opt-in and its env overrides are cwd-bound — both cause silent writes to the dev database), factories, HTTP/queue/DB assertions, and CI test jobs.
  • **`craft-cloud`** — When the project is hosted on Craft Cloud (detect via `craft-cloud.yaml` at the repo root or `craftcms/cloud` in `composer.json`). Required for plugin Cloud-compatibility constraints — `App::isEphemeral()` guards, asset-bundle CDN publishing, 15-minute queue-job cap, `csrfInput()` function over raw token output, and the `cloud/up` deploy lifecycle events.

Documentation

  • Extend guide: https://craftcms.com/docs/5.x/extend/
  • Class reference: https://docs.craftcms.com/api/v5/
  • Generator: https://craftcms.com/docs/5.x/extend/generator.html

Use `WebFetch` on specific doc pages when a reference file doesn't cover enough detail.

Common Pitfalls (Cross-Cutting)

  • Always use `addSelect()` in `beforePrepare()` — it's the Craft convention and safely additive when multiple extensions contribute columns.
  • Queue workers run in primary site context — use `->site('*')` for cross-site queries.
  • Including `id` in `getConfig()` — project config uses UIDs, never database IDs.
  • Business logic in models or controllers — services are where logic belongs.
  • Modules need manual template root, translation, and controllerNamespace registration — nothing is automatic.
  • `DateTimeHelper` in elements/queries, `Carbon` in services — never mix in the same class.
  • Hardcoding `/admin` in CP URLs — `cpTrigger` is configurable. Use `UrlHelper::cpUrl()` in PHP, `cpUrl()` in Twig.
  • Passing `$request->getBodyParams()` directly to `savePluginSettings()` on split-settings pages — only submitted keys persist, other settings are silently dropped. Load the full settings model first, update properties, then save.
  • **Naming a route or query param `token`** — it collides with Craft's reserved `tokenParam` and the request is rejected with a 400 before your controller runs. See `controllers.md` (Reserved request params).
  • **`EVENT_DEFINE_RULES` on `Entry::class` runs for every entry**, including nested entries the CP builds before `fieldId`/`ownerId` exist. `getType()`/`typeId` throws `Either \`sectionId\` or \`fieldId\` + \`ownerId\` must be set on the entry.` there, which breaks adding any nested entry. Always add the rule, decide in `'when'` behind an `isset($entry->fieldId) || isset($entry->sectionId)` guard, and validate with a `Validator` subclass. See `events.md` (Validation Rules on Core Elements From a Module).
  • **`project-config/write` empties `config/project/` and rewrites it from the DB.** On a stale local DB it deletes teammates' YAML. Run `craft up` first, require a clean `project-config/diff`, and check `git diff -- config/project` afterwards. Schema saves from PHP belong in a migration or console command, never a bare bootstrap script (YAML is only flushed on `EVENT_AFTER_REQUEST`). See `deployment.md` (project-config/write safely) and `migrations.md` (Project config writes outside a request).
  • Any non-underscore-prefixed template in a plugin's `templates/` dir is **directly routable in the CP**, bypassing your controller's `beforeAction()` gates. Underscore-prefix every template th
Read more
@michtio
Ships withcraftcms-claude-skills

Production-ready Claude Code skills, agents, and project templates for Craft CMS 5 development. Built and maintained by michtio.

8914 skills6 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
89
Star history of michtio/craftcms-claude-skills on GitHub, a chart by star-history.com

Other skills on craftcms-claude-skills.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about craftcms.

Which coding agents can use craftcms?
craftcms installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.