Skip to content

Powered by Grav + Helios

Grav Development

Grav Development

This page is for people who want to work on Grav itself or build things for it. It covers how to set up a development site, run the test suite, scaffold a plugin or theme, and contribute changes back. It applies to the core, plugins, themes, Admin Next and the API plugin.

If you maintain a plugin or theme from Grav 1.x and want to know what changed, start with the Developer Upgrade Guide and Plugin Compatibility instead.

The Parts of Grav 2.0

Grav 2.0 is spread across several repositories. Knowing which one owns what saves time when you go to fix something.

Part Repository What it is
Core getgrav/grav The system folder, the bin/grav CLI, and the base install.
API plugin getgrav/grav-plugin-api The REST API at /api/v1. Admin Next, the MCP server and any external tool talk to Grav through it.
Admin Next getgrav/grav-plugin-admin2 and getgrav/grav-admin-next The admin panel. The plugin repo is the PHP wrapper and the pre-built app. The grav-admin-next repo is the SvelteKit source.
MCP server getgrav/grav-mcp A Node.js app that lets AI clients drive a Grav site through the API. See MCP Server.
Plugins and themes One repository each For example grav-plugin-error or grav-theme-quark2.

The classic admin plugin is not part of Grav 2.0. Do not build new admin UI against it.

Requirements

  • PHP 8.3 or newer. The core is tested through PHP 8.5.
  • Composer, for installing dependencies and running the tests.
  • Git, with a GitHub account if you plan to send pull requests.
  • Node.js 18 or newer, only if you work on the Admin Next frontend, the MCP server, or a theme with a build step. Plain PHP and Twig work needs no Node.

Setting Up a Development Site

The normal install copies files into your web root. For development you want the web root to point at your Git clones, so a change in the clone shows up on the site straight away. Grav supports this with symlinks.

1. Clone the repositories into one folder

Put every repository you plan to work on in the same parent folder. The CLI tools look for them there.

BASH
mkdir -p ~/Projects/grav
cd ~/Projects/grav
git clone https://github.com/getgrav/grav.git
git clone https://github.com/getgrav/grav-plugin-api.git
git clone https://github.com/getgrav/grav-plugin-admin2.git
git clone https://github.com/getgrav/grav-plugin-error.git
git clone https://github.com/getgrav/grav-plugin-problems.git
git clone https://github.com/getgrav/grav-plugin-shortcode-core.git
git clone https://github.com/trilbymedia/grav-plugin-github-markdown-alerts.git
git clone https://github.com/getgrav/grav-theme-quark2.git

Tip

If you intend to contribute changes, fork the repository on GitHub first and clone your fork. The example above uses the upstream URLs for simplicity.

The plugins and theme listed here are the ones the core's .dependencies file expects. You can add any other plugin or theme repository to the same folder.

2. Tell Grav where the clones are

Create a file at ~/.grav/config with one line pointing at the parent folder:

YAML
github_repos: /Users/your_name/Projects/grav/

Both bin/grav new-project and bin/grav install read this file when asked to symlink.

Run new-project from the core clone with the -s flag. The destination is the folder your web server serves.

BASH
cd ~/Projects/grav/grav
composer install
bin/grav new-project -s ~/www/grav2

This creates the site folders, symlinks system, bin, index.php and composer.json back to the core clone, then symlinks each dependency from .dependencies into user/plugins and user/themes. Point your browser at the new site and you have a working Grav install where every edit happens in a Git repository.

To link another plugin or theme from the same parent folder into an existing site, use install with -p or -t:

BASH
cd ~/www/grav2
bin/grav install -p api
bin/grav install -p admin2
bin/grav install -t my-theme

The name is the part after grav-plugin- or grav-theme-. Running bin/grav install -s with no name re-links everything in .dependencies.

Note

Symlinked plugins still need their own Composer dependencies. If a plugin ships a composer.json, run composer install inside its clone.

Running the Core Tests

The core uses Codeception for unit tests and PHPStan for static analysis. Install the dev dependencies once, then run the suite from the core clone:

BASH
cd ~/Projects/grav/grav
composer install
composer test

To run a single test class or method, call Codeception directly:

BASH
vendor/bin/codecept run unit Grav/Common/InflectorTest
vendor/bin/codecept run unit Grav/Common/InflectorTest:testPluralize

Static analysis has its own scripts:

BASH
composer phpstan             # system/src at level 2
composer phpstan-framework   # Framework, Events and Installer at level 6
composer phpstan-plugins     # installed plugins at level 1

The full suite must pass before a pull request is merged. If you add behaviour, add a test in tests/unit alongside the existing ones.

Developing Plugins

Most Grav development is plugin development. A plugin is a folder in user/plugins with a PHP class that subscribes to events. That model has not changed in 2.0.

Scaffolding a new plugin

Install the DevTools plugin and run the wizard:

BASH
bin/gpm install devtools
bin/plugin devtools new-plugin

The wizard asks which Grav version to target and writes the matching compatibility block, Grav dependency and PHP requirement. The api template gives you a plugin that already registers an API route and a controller. Every option can be passed on the command line:

BASH
bin/plugin devtools new-plugin --grav=2.0 --template=api --name="My Plugin" --desc="What it does" --dev="Me" --email="[email protected]"

What is new for plugins in 2.0

  • Compatibility flags. Every plugin should declare which Grav versions it supports in blueprints.yaml. GPM and the migration tool read it. See Plugin Compatibility.
  • API endpoints. A plugin can register its own REST routes with the onApiRegisterRoutes event. See API Integration.
  • Admin Next UI. Custom fields, plugin pages, panels and menu bar items are web components backed by your API endpoints. Standard blueprint config pages need no extra work. See the API Developer Guide.
  • Translations. Admin Next reads plugin language files differently from the classic admin. See Admin Translations.
  • Markdown extensions. Plugins can add Markdown syntax through the new parser. See Markdown Extensions.
  • Twig sandbox. Twig in page content runs in a sandbox. If your plugin adds Twig functions or filters meant for content, they must be allowlisted. See the Developer Upgrade Guide.

When the plugin is ready for other people, follow GPM Submission. That page covers the required files, the changelog format, the release process and how to take over an abandoned plugin.

Developing Themes

A theme is a folder in user/themes with Twig templates, assets and a blueprints.yaml. The Themes chapter covers the details. DevTools can scaffold one:

BASH
bin/plugin devtools new-theme

The theme templates include options for Tailwind CSS and daisyUI with a Node build step, and a plain option with none. The copy template starts from a theme you already have installed.

Themes also carry a compatibility block. Add it before you publish, and see GPM Submission for the theme release checklist.

Working on Admin Next

Admin Next is two repositories. grav-plugin-admin2 is what gets installed on a site. It contains the PHP wrapper and a built copy of the app under app/. grav-admin-next is the SvelteKit source.

To change the UI, clone both as siblings and build from the plugin folder:

BASH
cd ~/Projects/grav
git clone https://github.com/getgrav/grav-admin-next.git
cd grav-plugin-admin2
./bin/build.sh

The script runs npm run build in the SvelteKit project and copies the output into app/. While you work, npm run dev:plugin inside grav-admin-next rebuilds into the plugin on every change.

The app is built for a fixed base path. If your admin route is not the default, pass the path when you build:

BASH
ADMIN2_BASE=/my-site/admin2 ./bin/build.sh

Admin Next has no direct access to Grav. Everything it does is an API call, so if you need new data in the admin you add an endpoint to the API plugin or your own plugin first. That is also how plugins extend the admin: see the API Developer Guide.

Working on the API Plugin

The API plugin is a normal Grav plugin. Symlink it into your development site with bin/grav install -p api, generate an API key from Admin Next, and test with curl:

BASH
curl -s https://grav2.test/api/v1/system/info \
  -H "X-API-Key: $GRAV_API_KEY" | jq

Send authentication in an X-API-Key header rather than Authorization. Some PHP-FPM setups drop the Authorization header before PHP sees it.

The API chapter documents every endpoint. If you add or change an endpoint, update its page under api-docs/ in the plugin so the reference stays accurate.

Contributing to the Core

Grav uses the GitFlow model. Day-to-day work happens on develop. Features go in feature/ branches that merge into develop. Releases merge develop into master and get a tag with a plain version number such as 2.1.6.

To send a change:

  1. Fork the repository and branch from develop.
  2. Make the change and add or update tests.
  3. Run composer test and composer phpstan. Both must pass.
  4. Open a pull request against develop. Say what the change does and why.

Report bugs in the repository that owns the code. Core bugs go to getgrav/grav. Admin bugs go to grav-plugin-admin2, API bugs to grav-plugin-api, and plugin or theme bugs to that plugin's or theme's own repository. If you are not sure, the Discord is the quickest place to ask.

AI-Assisted Development

Team Grav publishes Claude Code skills that encode the API and Admin Next integration patterns. They are useful for porting a 1.x plugin or scaffolding a new one. See AI-Assisted Development.

Further Reading

The Grav blog has a developer series that walks through the same ground with longer examples:

The free git-sync and license-manager plugins are complete, shipping examples of Admin Next integration and are worth reading end to end.