b10k / craft-component-guide
Visual Matrix block picker with live previews rendered from your own Twig templates — and a component library in the control panel.
Package info
github.com/b10k-nl/craft-component-guide
Type:craft-plugin
pkg:composer/b10k/craft-component-guide
Requires
- php: >=8.2
- craftcms/cms: ^5.0.0
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-13 06:17:05 UTC
README
Editors cannot tell which Matrix block is which. They pick “Cards Grid” from a dropdown of names, hope it is the right one, and ask a developer when it is not. Component Guide shows them a rendered preview of every block a page-builder field accepts, inside the Craft control panel, and lets them add the one they picked.
For developers it is a component library for Craft CMS — a living style guide of every Twig component in the project, rendered with your site's own CSS and discovered from the templates you already have. No separate Storybook, no second environment to keep alive, no screenshots to maintain.
It also catches previews that lie: a story can promise a field the entry type does not have, and the guide says so before an editor meets a card they can never reproduce.
Nothing configured yet? The guide starts by explaining how to get there:
Status:
1.7.0— stable. Lite is free, on any site, including production. Pro is $99 per site plus $39/year for updates after the first year, and is free to run in development and staging. New to it? GETTING-STARTED.md is a five-minute walkthrough.
Requirements
- Craft CMS 5.0+
- PHP 8.2+
What it does
- Recursively discovers components from a configured templates folder.
- Supports nested (
button/button.twig+button/button.stories.twig) and adjacent (button.twig+button.stories.twig) conventions. - Two story languages (Twig and PHP) and two shapes (simple + rich), normalized to one internal model.
- Groups, lists and client-side-searches components in the CP.
- Renders each story in an isolated iframe with your front-end CSS, on a desktop, tablet or phone frame, cropped to the component's own height.
- Generates a copy-pasteable Twig
{% include … with {…} only %}snippet. - Surfaces per-component errors without breaking the rest of the guide.
- Lists story-less templates via marker files (
GUIDE.md/BLOCKS.md/COMPONENTS.md), so the guide doubles as a full component inventory. - Blocks gallery for editors: a "Blocks gallery" button on every Matrix page-builder field (all view modes — blocks, cards, index) opens a panel of real component previews with descriptions; clicking one adds that block. In "Inline-editable blocks" mode the new block is prefilled with the story's own content, so it is visible on the page immediately. A block with several stories shows them in a dropdown on its card, or — with Variants as cards ticked — as one card per story, for stories that change the layout rather than a colour.
- Editors' own variants: "Save as my variant…" in a block's action menu keeps the block as the editor filled it, under a name, as a card of their own in the gallery. Personal, stored in their Craft user preferences — no table, no migration — and handed to a developer as a ready-to-paste story when it deserves to be one for everybody.
- Bulk setup with a coding agent: a recipe in
AGENT-SETUP.mdany agent can follow to document an existing library in one pass — marker file, one draft story per component, and a report of the blocks that do not match an entry-type handle. - Promote from the control panel: flip a reviewed component between
draftandstableon its card, and the guide tells you which files it changed so nothing is written behind git's back. - Persistent scan cache keyed by a filesystem fingerprint — invalidates itself the moment a story, template or marker changes.
When not to use this
- Your components query Craft themselves. A component that runs its own element queries needs a story that passes plain arrays instead; presentational components work as they are.
- You have no page-builder field. The editor half is built around Matrix. Without one the guide is still a component browser, but the half that earns its keep sits idle.
- You want screenshots you art-direct. Previews are re-rendered from the template every time, so they cannot go stale — and cannot be posed either.
- Your previews need CSS the guide cannot reach. They are exactly as accurate as the stylesheet you point them at.
- You need a verdict on every component. The contract check gives none when it cannot identify a single adapter. Refusing beats guessing.
Editions
Lite is free. Everything that tells you the truth about your own project is in it:
- the component index, marker files and groups
- live previews rendered with your site's own CSS
- the contract check and its badge — a story promising a field the entry type does not have
- loud render errors, rather than a silent broken thumbnail
- the story scaffolder, including the whole-library pass
- the write journal
Pro adds the things the plugin does on your behalf:
- inserting a block from the gallery — an editor picks a card and the block lands filled in, from the state whose preview they were looking at
- saving their own variants — "Save as my variant…" on a block, and the card it becomes in the gallery (see below)
stories/check— the contract check as a console command with an exit code, for CI. The edition comes from your committedproject.yaml, so a CI runner reads Pro without needing a licence key of its own; under Lite the command explains itself and exits 0 rather than breaking your build
The blocks gallery opens in Lite too. Editors browse the real rendered cards for the blocks a field accepts; only the click that inserts a filled-in block is held back, and Craft's own “New Block” menu works exactly as it always did.
The split is seeing versus acting, not developer versus editor. A component library you cannot see is the problem this plugin exists to solve, so seeing it is not the part with a price on it.
In PHP and Twig:
if (\b10k\componentguide\Plugin::getInstance()->isPro()) { … }
Editors' own variants
The developer's stories are the starting points. An editor who keeps building the same block the same way — the dark coaching hero, the two-plan pricing table — can keep it:
- Fill in a block on any entry as usual.
- Open the block's action menu (⋯) and choose Save as my variant….
- Name it. The panel says which fields are kept before anything is saved.
The variant appears in the blocks gallery next to the block's stories, marked ★, with a live preview. Clicking it adds the block filled in exactly as saved.
What is kept. Only the fields the gallery can fill back in: those the adapter passes to exactly one template argument — text, number, URL, email, dropdown, radio buttons and lightswitches, up to 500 characters each. Rich text (CKEditor), assets, relations and nested blocks are not kept and start from the block's first story instead; the save panel names them. That rule is what keeps a variant honest: its card previews exactly what a click puts back.
Where it lives. In the editor's own Craft user preferences — personal, across sessions and devices, gone when the user is deleted. The plugin still ships no migration and no table of its own. Nobody else sees an editor's variants, developers included.
Making it everyone's. On the variant's gallery card, Share with a developer copies it as a story entry in Twig — every argument written out — for a developer to paste into the component's story file. That hand-over is deliberate: preferences are personal and cannot be searched efficiently, and a story in git is reviewed like any other change. Delete removes the variant.
Saving a variant is a Pro feature. It works in Matrix fields set to Inline-editable blocks; Cards and Index modes have no block menu to add it to.
Installation
From the Craft Plugin Store: open Plugin Store in your control-panel sidebar and search for “Component Guide”.
Or with Composer:
composer require b10k/craft-component-guide php craft plugin/install component-guide
On DDEV, prefix each command with ddev.
New to it? GETTING-STARTED.md walks through the first five minutes.
Configuration
Settings can be edited in Settings → Plugins → Component Guide, or overridden per-environment with a config file. The config file wins; any field it sets is read-only in the CP.
config/component-guide.php:
<?php return [ 'componentPath' => '', // scan root, relative to templates/ ('' = whole dir) 'storySuffix' => '.stories.php', 'enableCpSection' => true, 'enableIframePreview' => true, 'previewCss' => ['/dist/css/app.css'], // string or array 'previewJs' => ['/dist/js/app.js'], 'previewTemplate' => '', // site template rendered into the preview <head> ];
| Setting | Default | Notes |
|---|---|---|
componentPath |
'' (whole templates/) |
Scan root relative to templates/. Empty scans everything; set e.g. _components to narrow. No absolute paths or ... |
storySuffix |
.stories.php |
Must end in .php. The Twig equivalent (.stories.twig) is derived from it automatically and discovered too — that's the format the scaffolder writes. |
enableCpSection |
true |
Show/hide the CP nav item. |
enableIframePreview |
true |
Isolated iframe vs. an "open preview" link. |
previewCss / previewJs |
[] |
Front-end assets injected into the preview. |
previewTemplate |
'' |
A site template rendered into the preview document's <head> — the right place for Vite/manifest asset tags (see below). |
The model is Storybook-style: the scanner walks the scan root recursively and
shows every Twig template that has an adjacent story file — anywhere in the
tree (components, page-builder blocks, partials). A template's author opts it into
the guide simply by dropping a *.stories.twig (or *.stories.php) next to it;
nothing else registers it. Components are grouped by their folder path (or by a
story's meta.group).
Directory conventions
templates/_components/
├── button/
│ ├── button.twig
│ └── button.stories.twig
├── card.twig # adjacent convention also works
├── card.stories.twig
└── navigation/menu/
├── menu.twig
└── menu.stories.twig
A Twig file is listed as a component only when it has a matching story file —
unless its folder is opted in via a marker file (see below).
Hidden files/dirs, node_modules, vendor and cache folders are ignored.
Marker files: components without stories
Writing a story per component is an upgrade, not the ticket in. Drop a marker
file — GUIDE.md, BLOCKS.md or COMPONENTS.md — into any folder inside the
scan root, and every plain Twig template in that folder and its subfolders
is listed in the guide, story or not. Story-less templates appear as inert
“undocumented” cards with a hint to add a story; templates whose filename
starts with _ are treated as internal partials and skipped.
The marker is also lightweight documentation:
# Content Blocks Reusable page blocks editors can add through the Matrix page builder. ## Anything below the intro is ignored by the guide — document freely.
- The H1 becomes the group label. Group names mirror the folder hierarchy:
a marker's H1 replaces its own folder's name in the chain and plain
subfolders are humanized (
Components / Cards). Components without an explicitmeta.groupinherit the derived name, so documented and undocumented cards share one section. - The intro text below the H1 (up to the next heading) becomes the group description on the index page.
- One marker per folder; with duplicates, precedence is
GUIDE.md→BLOCKS.md→COMPONENTS.mdand the CLI scan warns. - Not listed as components: files starting with
_(internal partials) andindex.twig/undefined.twig(the dispatcher pattern's entry point and fallback). A story file next to any of them still documents it explicitly. - Markers live in git next to your templates and read as plain folder docs on GitHub. The scan cache tracks them automatically.
Story format
Stories come in two languages — Twig (*.stories.twig) and PHP
(*.stories.php) — with the same shape. Twig is the default: it's the language
the component is already written in, and it's what the scaffolder writes. Both
are discovered automatically; pick per component if you like.
Twig
{% set meta = {
title: 'Button',
group: 'Atoms',
description: 'Primary user-action button.',
status: 'stable',
} %}
{% set stories = {
'Primary': {
args: { label: 'Save', variant: 'primary' },
description: 'The default call to action.',
},
'Secondary': {
args: { label: 'Cancel', variant: 'secondary' },
},
} %}
meta is optional — a file with just stories works. The story file is
rendered as a data-only template: set those two variables and output nothing.
PHP — simple
<?php return [ 'Primary' => ['label' => 'Save', 'variant' => 'primary'], 'Secondary' => ['label' => 'Cancel', 'variant' => 'secondary'], ];
PHP — rich (with metadata)
<?php return [ 'meta' => [ 'title' => 'Button', 'group' => 'Atoms', 'description' => 'Primary user-action button.', 'status' => 'stable', ], 'stories' => [ 'Primary' => [ 'args' => ['label' => 'Save', 'variant' => 'primary'], 'description' => 'The default call to action.', 'background' => '#f5f5f5', 'viewport' => 'phone', 'tags' => ['action', 'form'], ], ], ];
Optional story keys: args, description, background, viewport, tags.
viewportpicks the device the preview opens in —desktop(default),tabletorphone. The reader can still switch; this just says which state the story is really about, so a mobile-only variant doesn't open in desktop.backgroundsets the colour behind the preview frame (any CSS colour), useful for components meant to sit on a dark or tinted section.
Status vocabulary: stable, beta, draft, deprecated (case-insensitive;
wip, ready, experimental and legacy are accepted as aliases). Only
stable components are addable from the blocks gallery — see below.
Component example
{# templates/_components/button/button.twig #} {% set label = label ?? 'Button' %} {% set variant = variant ?? 'primary' %} {% set disabled = disabled ?? false %} <button type="button" class="button button--{{ variant }}"{% if disabled %} disabled{% endif %}> {{ label }} </button>
Prefer plain arrays and scalar props. Craft element objects returned by trusted story files are not blocked, but arrays keep stories portable.
Scaffolding stories
Undocumented components (see marker files above) can bootstrap their own
story. Each undocumented card gets an Add story button in environments
where Craft's allowAdminChanges is on (local and staging by convention — the
same gate Craft uses for its own project-file changes); elsewhere the guide
says so instead of hiding the feature. The equivalent on the command line is:
php craft component-guide/components/make hero # writes hero.stories.twig php craft component-guide/components/make hero --states # one story per detected state php craft component-guide/components/make hero --format=php
The scaffolder reads the template and guesses a Default story from it:
- root variables become args;
{% for it in items %}becomes a three-item sample array carrying the keys the loop actually uses (it.title→title); |default('…')and the{% set x = x ?? '…' %}idiom supply real values;- everything else is guessed by name —
*Url→#, image-ish*Url→ an inline SVG placeholder,*Html→ a paragraph,is*/has*→true; - the first sentence of the template's leading
{# … #}comment becomes the component description.
One story, or one per state. If the template switches on a value —
theme == 'dark', mediaPosition == 'right' — those comparisons are its
states, and a second button appears next to Add story: Add 2 stories
(however many it found), with a tooltip naming the values. Taking it writes
one story per value (Light, Dark, Media right…) instead of a single
Default, so the component's variants are documented from the start. Either
way it is one story file — the buttons differ only in how many stories go
inside it.
The result is a draft: it is written with status: draft, the args are
guesses, and an existing story file is never overwritten. Review it, fix what
the heuristics got wrong, and promote the status when the component is
properly documented.
Documenting a whole library with a coding agent
Scaffolding one component at a time is fine for a handful. For a library that already has forty templates, the format is documented well enough that an agent can write the stories for you — so the package ships a recipe for exactly that:
Read AGENT-SETUP.md in vendor/b10k/craft-component-guide and follow it.
It is tool-agnostic — nothing in it names a particular agent — and it is a plain
Markdown file in the package root, so it is on disk as soon as Composer has run.
The recipe tells the agent to find the component folders and ask which to
document, read entry-type handles from config/project/ rather than inventing
them, write one story per component (plus one per switchable state), use
placeholder tokens for what carries no information and real copy where the
story asserts something — and to stop there:
- it never renames a template or an entry type to force a gallery match; the mismatches go into its report instead, which is the most useful thing it hands back;
- it never touches your component templates or
config/project/; - it never commits — the diff is the point;
- every story it writes is
status: draft. Onlystablecomponents are addable from the blocks gallery, so after an agent pass your own index is full of previews while editors see nothing new. That is deliberate: a generated story markedstablewould be a claim nobody had checked.
Then you review — not by reading a large diff, but by looking. Open the guide, scan the rendered previews, and the ones that came out wrong are the ones whose args need a two-line fix.
Reviewing and promoting
A component whose story is draft gets a Mark stable button on its index
card and on its own page; a stable one gets Back to draft. Both write the
one status line in the story file and leave every other byte of it alone, so
your comments, spacing and argument order survive.
Only those two statuses are toggleable. beta and deprecated say something
about a component's lifecycle that only a developer knows, so they stay in the
editor where you wrote them; the toggle is for the review verdict, which is the
one status change you make in bulk.
The same two gates as scaffolding apply (allowAdminChanges, and a writable
templates directory), so the button is absent — with a stated reason — on
read-only environments.
What the guide changed
The control panel has no git status. So when it writes into templates/ —
a scaffolded story, a promoted status — it says so: a notice on the index lists
the files it changed, until they are dealt with.
It only ever claims what it can still verify. Each entry records the content
hash of what was written, and is re-checked on every read: if the file's hash no
longer matches, someone edited or reverted it after the guide did, and the entry
is dropped. If .git/index is newer than the file, a git add or commit
happened since, and the entry is dropped. Reviewed drops it by hand. The
notice would rather miss a file than cry wolf — one that lies twice is one you
stop reading, and git remains the real safeguard.
The record lives in Craft's runtime storage, never in your repository.
Placeholder tokens
Stories don't have to carry their own copy. Any string arg can be a token, expanded when the preview renders:
{% set stories = {
'Default': {
args: {
heading: '@lorem_w_6',
bodyHtml: '@lorem_p_2',
imageUrl: '@image_1600x600',
iconUrl: '@icon_star',
},
},
} %}
| Token | Result |
|---|---|
@lorem |
a short phrase |
@lorem_w_12 |
12 words |
@lorem_s_3 |
3 sentences |
@lorem_p_2 |
2 paragraphs, wrapped in <p> |
@image |
a photo, 800×600 |
@image_1600x600 |
a photo at that size |
@icon |
one of Craft's system icons |
@icon_star |
that specific system icon |
Three things worth knowing:
- Deterministic. Values are derived from the component, story and argument
path — the same story always renders the same text, so previews don't
flicker and gallery thumbnails match the detail page. Items in a list still
differ from each other, so three identical
@lorem_w_4tokens produce three different samples. - Offline-safe. Photos come from an external service; if it can't be reached, the preview falls back to an inline placeholder of the same size. Icons are always inline (no network).
- Unknown tokens pass through.
@somethingthe plugin doesn't recognise is left exactly as written.
The story scaffolder emits these tokens, which is why generated stories stay short and readable.
What to tokenise — and what not to
Tokens exist to kill busywork, not meaning. A useful rule:
- Tokenise what carries no information: photography, icons, “just some paragraph of text” in a fresh scaffold, the third and fourth items of a list.
- Write real copy where the story asserts something: the flagship state you'd screenshot, and the edge cases you're documenting on purpose — a heading long enough to wrap, a button label that nearly overflows, a card with no image.
A preview full of lorem tells you the component renders. A preview with real copy tells you it works — that the two-line heading still fits, that the tone is right, that “Membership” doesn't break the button. That's also what makes the guide worth showing to a client or a new teammate.
So: scaffold with tokens (one click, previews come alive), then replace the
values that are worth a human's attention. That's exactly what the draft
status is there to remind you of.
Recommended architecture: adapters + presentational components
The guide works with any template structure, but this three-layer pattern gets the most out of it — and keeps a Matrix page builder maintainable:
templates/_v2/
├── _blocks.twig # dispatcher — loops the Matrix field (~10 lines)
├── _adapters/ # entry-aware glue (NOT listed in the guide)
│ ├── hero.twig # one per block type, named after its handle
│ ├── promoBanner.twig
│ └── undefined.twig # fallback: dev/preview-only “unmapped block” alert
└── _blocks/ # presentational components (listed in the guide)
├── BLOCKS.md # marker file — inventory + group description
├── hero.twig # pure: accepts scalars/arrays, never an Entry
├── hero.stories.twig # preview states for the guide
└── promoBanner.twig
Presentational components accept plain scalars and arrays with |default
fallbacks — never a Craft element. That is exactly what makes them previewable
in the guide with simple array stories, and reusable outside the Matrix
context.
Adapters are the only layer that touches entries: one small template per
block type, receiving { block }, doing all field access, getFieldByHandle
guards, image transforms, queries and fallbacks — then including the
presentational partial. They contain no markup of their own. Keep the folder
outside any marker-covered subtree so the guide never lists glue code as
components.
The dispatcher collapses to a loop with a convention-based include and an automatic fallback (Twig picks the first template that exists):
{% for block in blocks.all() %}
{% include [
'_v2/_adapters/' ~ block.type.handle ~ '.twig',
'_v2/_adapters/undefined.twig',
] with { block: block } only %}
{% endfor %}
undefined.twig renders a visible warning in dev mode / live preview and
nothing in production — a new block type shows up as an actionable alert
instead of silently rendering nothing.
Rules of thumb: a component never touches an Entry; an adapter never contains markup; adding a block = entry type + adapter + component (+ story when it earns one).
Preview asset configuration
Set previewCss / previewJs (string or array of URLs) to load your compiled
front-end assets into the preview document. With none configured you get a clean,
unstyled document. CSS support matters most; JS is optional.
Using Vite (or any manifest-based build)? Static URLs go stale between dev
and production — point previewTemplate at a small site template instead, and
put your asset tags there. It is rendered into the preview document's <head>
in site mode, so helpers like craft.vite resolve the dev server vs. the
built manifest exactly like on the front end — previews get real styling and
working component JS (carousels included) in both environments:
{# templates/_component-guide-preview.twig #} {{ craft.vite.script('src/js/app.ts') }}
// config/component-guide.php 'previewTemplate' => '_component-guide-preview',
Uninstalling
1. Remove the plugin
Either from the control panel — Settings → Plugins → Component Guide → ⚙ → Uninstall — or from the command line:
php craft plugin/uninstall component-guide composer remove b10k/craft-component-guide
That removes the plugin, its settings, its permission and its scan cache.
2. Decide what to do with your story files
They are left in place on purpose. A *.stories.twig next to a template is
inert without the plugin — nothing includes it, nothing renders it, it costs
nothing. A GUIDE.md is just a markdown file that reads as folder
documentation on GitHub. Keeping them means reinstalling later picks up exactly
where you left off.
If you do want them gone, delete them yourself — they're your files, in your repository, and you should see the diff before it lands:
In your editor (PhpStorm, VS Code, …)
- Search the project for
*.stories.twigand*.stories.php(PhpStorm: ⌘⇧O → type.stories.· VS Code: ⌘P → same). - Review the list — some of these may be files you wrote or heavily edited.
- Select and delete.
- Search for
GUIDE.md,BLOCKS.mdandCOMPONENTS.mdinside your templates folder and delete those too, if you don't want them as folder docs. - Commit, so the removal is reviewable and revertable like any other change.
In a terminal
# preview first — nothing is deleted by this find templates -name '*.stories.twig' -o -name '*.stories.php' # then remove them through git, so the change is staged and revertable git rm 'templates/**/*.stories.twig' 'templates/**/*.stories.php'
Marker files, if you want those gone too:
git rm 'templates/**/GUIDE.md' 'templates/**/BLOCKS.md' 'templates/**/COMPONENTS.md'
What was never touched
Your content, your Matrix fields, your entry types, your templates. The guide
only ever reads them. The exceptions are story files, and only when you ask:
the Add story button (and its CLI equivalent) writes a new one and never
overwrites an existing one, and Mark stable / Back to draft rewrite the
single status line of one.
Editors' saved variants stay in their user preferences, under one key
(componentGuide.stories), unread by anything once the plugin is gone.
The scan cache can also be cleared on its own at any time, without uninstalling anything: Utilities → Caches → Component Guide scan cache.
Security & trust model
- Story files are project code — Twig templates or PHP files in your repo.
Treat them like any other template. They are only ever discovered inside the
configured
componentPath. - The scanner refuses absolute paths and
..traversal; the resolved directory must live inside your templates folder. - Previews render only component/story IDs resolved through the repository — a request can never supply a raw template path.
- Every CP/preview action requires login and the
component-guide:accesspermission. - The two actions that write into
templates/— scaffolding a story and changing a status — are POST-only, CSRF-protected, and gated onallowAdminChangesplus a writable templates directory. A status change rewrites exactly one line. Every such write is recorded and surfaced on the index (see What the guide changed), because a control panel that edits project files silently is worse than one that cannot. - Previews render in a separate iframe document, so project CSS and JS never
mix with the control panel's. It is not a
sandboxed iframe: it shares the control panel's origin, which is how the gallery and the component page measure a preview's height. Metadata is escaped; error messages appear whereallowAdminChangesis on, absolute paths and stack traces only indevMode. - Editors' variants are read and written only for the signed-in user, in their own user preferences; no request can name another user's. Saving is POST-only and CSRF-protected, keeps only fields the block's adapter maps, and renders through the same preview as stories — a variant never supplies a template path or args of its own.
Do not expose the plugin to untrusted users — story files are project code, not a sandbox.
Troubleshooting
- "No components found." — check
componentPath(relative totemplates/) and that story files end instorySuffix(or its.twigequivalent). The index also walks you through this when nothing is discovered yet. - Component missing — it needs a matching
*.twignext to its story file. - Render error in preview — the component threw; the message is shown
wherever
allowAdminChangesis on, and the full trace indevMode. - A story renders nothing — usually markup behind a condition the args don't satisfy, or a template that reads Craft data itself; the preview says so instead of showing a blank frame.
- Styles missing in preview — set
previewCss, orpreviewTemplateif you build with Vite. - No "Add story" button — scaffolding writes files, so it needs
allowAdminChanges; the index says so where that's off. - No "Blocks gallery" button — the field's entry-type handles must match
component names. Matching ignores case and separators (
inline-donation-form↔inlineDonationForm), so the usual cause is a genuinely different name; opencomponent-guide/picker-mapto compare.
Development
composer install composer test # PHPUnit (scanner, story parser, scaffolder, placeholders) composer analyse # PHPStan (level 5) composer check # both npm install # once (Node 22+) npm test # control-panel JavaScript: gallery prefill and cards (jsdom)
CLI diagnostics
Run the same discovery the CP uses, to verify configuration without opening the control panel:
php craft component-guide/components/scan # DDEV: ddev exec php craft component-guide/components/scan
Roadmap
- Page presets — a developer-defined set of blocks (“service page”, “campaign landing”) an editor adds in one go, filled from the stories
- Craft 6 — a 6.x release as soon as Craft 6 is stable
- Seeding scaffolded args from the matched entry type's field layout, so the draft comes from the schema instead of a guess
Feedback
Bug reports, feature ideas and “this felt weird” notes are all welcome — open an issue. GETTING-STARTED.md lists the current known limitations.
Contributing
See CONTRIBUTING.md. Report security issues privately (see that file) rather than via public issues.
License
The Craft License — the standard licence for commercial Craft plugins: source-available, one licence per production environment. Lite is free everywhere; Pro is free to run in development and staging.



