Skip to content

How to Build a Grav Plugin: Part 3

Setting up a dev environment

  • code
  • tutorial
  • series

So far in this series, I covered:

  1. identifying the need for, and desired features of, this project, and
  2. sketching out the syntax I’m intending to use.

Time to get to work building the thing.

Installing pre-requisites

I’m on a Windows machine, so my instructions will be biased toward that, and my own setup in particular.

For alternatives, your best bet is the [requirements section of the Grav documentation].

Package manager (OPTIONAL)

You’re welcome to install these dependencies however you like, including going to each site, downloading and running an installer, and then editing your PATH manually so you can call programs from the terminal.

I can happily recommend using the Scoop package manager on Windows. “Scoop installs programs from the command line with a minimal amount of friction”: it eliminates UAC popups, handles things like adding items to your PATH, and is generally quite easy to use.

If you’re running MacOS (or a Linux distribution), you’ll have a similar experience using Homebrew; software package management on Linux is already pretty fantastic.

My instructions will assume you’re using Scoop or Homebrew; if you’re just getting started, I’d suggest you follow along that way.


Have a look at Scoop’s README on GitHub for more information about the project and how it works. To just get up and running, first install scoop by running the following from a PowerShell window:

Invoke-Expression (New-Object System.Net.WebClient).DownloadString('')

You should expect to see something like the following:

Downloading scoop...
Creating shim...
Downloading main bucket...
Scoop was installed successfully!
Type 'scoop help' for instructions.
If you get an error, you probably need to change the execution policy. Run Set-ExecutionPolicy RemoteSigned -Scope CurrentUser in the same PowerShell window, then try the install command again.

Notice that the installer downloads the main bucket. Buckets are lists of software, stored as JSON files. We’ll also need two other buckets to get access to VS Code and a particular version of PHP.

scoop bucket add extras; scoop bucket add versions

You should see the following:

Checking repository...
Cloning bucket repository...
The extras bucket was added successfully.
Checking repository...
Cloning bucket repository...
The versions bucket was added successfully.

Perfect: you’re all set to install the other dependencies.


Head over to to learn more about the project. The TLDR version to get up and running with Homebrew is to run the following in your terminal:

/bin/bash -c "$(curl -fsSL"

Text editor

I’ve been quite happy using Visual Studio Code for a few years now, although any text editor will do. If you already have one that you’re familiar with, cool — use that. I don’t have any particularly strong opinions here; some people will.

I’m of the mind that you should spend a minimum amount of time worrying about which editor to use; instead, just pick something and spend your time learning how to use whatever you choose. VS Code is free to use, cross-platform, has a huge mindshare with a rich theme and plugin ecosystem, is in active development, etc. If you’re concerned about telemetry, replace “VS Code” with “vscodium” throughout.

Throughout this series, if I give an instruction to do something in VS Code, first open the Command Palette (Ctrl+Shift+P), then type in the command and press Enter. I’ll also give the keyboard shortcut if you prefer to use that. Using the Command Palette might take some getting used to if you’re accustomed to reaching for the mouse, but it’s worth getting over the small mental hurdle.

If you need a crash-course in using VS Code, I’d suggest Microsoft’s video series Getting started with Visual Studio Code. The first two videos in their series are embedded below.

Video: Getting started with Visual Studio Code
Video: Code editing in Visual Studio Code


Note: I’m still using PHP version 7.4. PHP 8 was released in November 2020, and is fully supported by Grav. I’ll provide installation instructions for both versions; you can safely use either.

Or for PHP 8:

Links to installers for both versions for all platforms are available from


Composer is a dependency manager for PHP, similar to npm for Node.js or pip for Python. We’ll use Composer as part of the scaffolding of the plugin, and to bring in the Highlight.php library.

Version Control Software (Git)

To release a theme or plugin to the Grav repository, you’ll need a GitHub account. There are a number of version control tools available;Some free-to-use alternatives include Fossil, Mercurial, Pijul, and Subversion GitHub only supports Git. Fortunately, Git seems to the most popular by a good margin, so this highly transferable if you’re just getting started.

The usual incantations apply:

I won’t be covering much beyond some basic operations regarding Git or GitHub, although I’ll do a quick walkthrough of creating a new repository on GitHub and connecting your existing local repository. If Git/GitHub are new to you, there are thousands of resources already published online that some quick searching with any search engine will turn up.

The Git Handbook published by GitHub is a good first stop if you prefer reading; it has a number of links to other resources as well. If you prefer video, there are a number of popular YouTube offerings in the 15-minute to 1-hour range in first page of results here.


Head over to to sign up. No cost for a lot of use cases, including this one. I’d recommend setting up 2FA while you’re at it.

Grav itself

The last prerequisite. There are a few ways to install Grav; I prefer the simplest.

Go to; click on the button to download the Grav Core + Admin Plugin. At the moment, the latest release is version 1.7.z; this whole process will likely work with any major version (1.y.z). Grav follows SemVer, which is a version numbering system that has the convention MAJOR.MINOR.PATCH. You can read more about semantic versioning at

Unzip the archive you just downloaded, then open the ‘grav-admin’ directory in VS Code. From VS Code, run the File: Open Folder command (Ctrl+K Ctrl+O).

In the Explorer pane, you should have something very similar to the following:

Explorer pane of VS Code showing directory hierarchy of freshly unzipped Grav Core + Admin archive
Explorer pane of VS Code showing directory hierarchy of freshly unzipped Grav Core + Admin archive

Grav: DevTools Plugin

The Grav core team maintains a DevTools plugin that takes care of some of the boilerplate when creating a new plugin, theme, or blueprint.

Open a new terminal from within your VS Code session using the Create New Terminal command (Ctrl+Shift+`). From there, install the plugin by running php bin/gpm install devtools. After a moment, you should see something along the lines of:

Preparing to install DevTools [v1.5.4]
  |- Downloading package...   100%
  |- Checking destination...  ok
  |- Installing package...    ok
  '- Success!

Scaffolding the plugin

We’re just about ready to tuck into actually coding the plugin. Now that we have the DevTools plugin installed, from the terminal, run php bin/plugin devtools new-plugin and answer the prompts.

Enter Plugin Name:
 > highlight-php
Enter Plugin Description:
 > Server-side syntax highlighting (i.e., without JavaScript) using the [highlight.php]( library
Enter Developer Name:
 > Iain Gillis
Enter GitHub ID (can be blank):
 > iainsgillis
Enter Developer Email:

SUCCESS plugin highlight-php -> Created Successfully

Path: D:/projects/grav-admin/user/plugins/highlight-php

Please run `cd D:/projects/grav-admin/user/plugins/highlight-php` and `composer update` to initialize the autoloader

Go ahead and follow the instructions on the last line of the output to change into the newly created plugin directory and initialize the autoloader. Autoloading in PHP is a convenience that relies on conventions in directory structure and filenaming to saves you having to write require and include over and over again. If you want to know more, MetaBox has a really good introduction on their website

Before running composer update, your directory tree will look like this:

├── classes
├── blueprints.yaml
├── composer.json
├── highlight-php.php
├── highlight-php.yaml
└── languages.yaml

1 directory, 8 files

Running composer update yields the following output:

Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Writing lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files

And now your plugin directory looks like this, with a new composer.lock file and a vendor directory:

├── classes
├── vendor
│   ├── composer
│   │   ├── ClassLoader.php
│   │   ├── InstalledVersions.php
│   │   ├── LICENSE
│   │   ├── autoload_classmap.php
│   │   ├── autoload_namespaces.php
│   │   ├── autoload_psr4.php
│   │   ├── autoload_real.php
│   │   ├── autoload_static.php
│   │   ├── installed.json
│   │   ├── installed.php
│   │   └── platform_check.php
│   └── autoload.php
├── blueprints.yaml
├── composer.json
├── composer.lock
├── highlight-php.php
├── highlight-php.yaml
└── languages.yaml

3 directories, 21 files

Phew! Good for you if you’ve made it this far, especially if you’re new to this. 😄 There’s a little bit of setup left on the version control, but let’s split that out into a separate post.