Fork the repo
First, you need to create a fork of the repo. A fork is a copy of the repo where you can make changes before sending them back with a request to be merged into the original repo.
Head to the cms repo and click the "Fork" button at the top right.
Clone it
Once you have a fork, you can clone it on your local machine with git. It can go anywhere - you probably already have a folder where your projects live. Most people use ~/Sites/
or ~/Code`.
cd Code git clone https://github.com/your-username/cms.git Cloning into 'cms'...remote: Enumerating objects: 86396, done.remote: Counting objects: 100% (3025/3025), done.remote: Compressing objects: 100% (1917/1917), done.remote: Total 86396 (delta 1674), reused 2078 (delta 1085), pack-reused 83371Receiving objects: 100% (86396/86396), 33.39 MiB | 5.76 MiB/s, done.Resolving deltas: 100% (67201/67201), done.
Create a sandbox project
The cms
repo is just the Laravel package — it can't run on its own. It needs to be installed into a Laravel app.
The easiest way to set this up is to install a Starter Kit. In a separate folder, create your site:
cd sites statamic new sandbox Creating a statamic/statamic project at ./sandbox[✔] Statamic has been successfully installed into the sandbox directory.Build something rad!
Link your fork to the sandbox
At this point, your sandbox app is going to be using the "real" version of Statamic. You'll need to tell it to use your local fork.
In your app's composer.json
, add a repositories
array with a "path" repository pointing to where you cloned your fork earlier:
{ "name": "statamic/statamic", "type": "project", "description": "Statamic", "keywords": ["statamic", "cms", "flat file", "laravel"], "require": {
"php": "^8.2", "laravel/framework": "^11", "laravel/tinker": "^2.9", "statamic/cms": "^5.0" }, "require-dev": {
"barryvdh/laravel-debugbar": "^3.8.1", "fakerphp/faker": "^1.23", "laravel/pint": "^1.13", "laravel/sail": "^1.26", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.0", "phpunit/phpunit": "^11.0", "spatie/laravel-ignition": "^2.4" }, "autoload": {
"psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } }, "autoload-dev": {
"psr-4": { "Tests\\": "tests/" } }, "scripts": {
"pre-update-cmd": [ "Statamic\\Console\\Composer\\Scripts::preUpdateCmd" ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi", "@php artisan statamic:install --ansi" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "post-create-project-cmd": [ "@php artisan key:generate --ansi", "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"" ], "post-update-cmd": [ "@php artisan vendor:publish --tag=laravel-assets --ansi --force" ] }, "extra": {
"laravel": { "dont-discover": [] } }, "config": {
"optimize-autoloader": true, "preferred-install": "dist", "sort-packages": true, "allow-plugins": { "pestphp/pest-plugin": true, "php-http/discovery": true, "pixelfear/composer-dist-plugin": true } }, "minimum-stability": "dev", "prefer-stable": true, "repositories": [ { "type": "path", "url": "/path/to/cms" } ] }
Next, require the branch of cms
you checked out:
composer require "statamic/cms 5.x-dev"
(We'll go into more detail in a moment on what constraint should be used there.)
In the output, you should see it symlinks the cms
directory to your fork:
./composer.json has been updatedRunning composer update statamic/cms> Statamic\Console\Composer\Scripts::preUpdateCmdLoading composer repositories with package informationUpdating dependenciesLock file operations: 0 installs, 1 update, 0 removals - Upgrading statamic/cms (v5.7.3 => 5.x-dev) Writing lock fileInstalling dependencies from lock file (including require-dev)Package operations: 0 installs, 1 update, 0 removals - Removing statamic/cms (v5.7.3) - Installing statamic/cms (5.x-dev): Symlinking from /path/to/cms - Downloading statamic/cms (dist) Failed to downloadGenerating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump> @php artisan package:discover --ansiDiscovered Package: ajthinking/archetypeDiscovered Package: barryvdh/laravel-debugbarDiscovered Package: intervention/imageDiscovered Package: laravel/sailDiscovered Package: laravel/tinkerDiscovered Package: nesbot/carbonDiscovered Package: nunomaduro/collisionDiscovered Package: nunomaduro/termwindDiscovered Package: rebing/graphql-laravelDiscovered Package: spatie/laravel-ignitionDiscovered Package: statamic/cmsDiscovered Package: wilderborn/partyline> @php artisan statamic:install --ansiDiscovering addons.Publishing [statamic] assets.Publishing [statamic-cp] assets.Publishing [statamic-frontend] assets.Compiled views cleared successfully.Application cache cleared successfully.97 packages you are using are looking for funding.Use the `composer fund` command to find out more!
You can confirm it by checking the path to the package:
composer show statamic/cms --path statamic/cms /path/to/cms
Use an appropriate branch
Be sure to work on a new, dedicated branch for your Pull Request. Among other things, it'll make it easier for the Statamic team to push minor changes if necessary (like fixing typos, code style, tweaks, and so on). We request that you add "feature" or "fix" in the branch name so it's easier to understand the intent of your PR.
git checkout -b feature/new-thinggit checkout -b fix/issue-9999
When requiring the cms
package, it's important to require the appropriate constraint. If you don't use the right one, Composer may decide to use the real cms
package, and you'll be left wondering why your code changes aren't appearing.
If the branch is numeric then you need to require BRANCH.x-dev
(e.g. a branch named 5.x
should use a constraint of 5.x-dev
).
Otherwise, you'll need to use dev-BRANCH
(e.g. a branch named feature/mybranch
should use a constraint of dev-feature/mybranch
).
Once you've done the initial symlink, you can change cms
branches freely. However once again, be aware if you do a composer update
or require
, you may end up with a live version of cms
.
Dealing with assets
If your contribution involves Control Panel assets - Stylesheets, JavaScript, or Vue components - you'll need to compile them and have them used by your sandbox app. You can do this with another symlink.
In your sandbox, delete the public/vendor/statamic/cp
directory, which should have been created when you initially created the site.
Compile the assets within the cms
repo.
cd cmsnpm cinpm run dev # or npm run build
The assets will be compiled into cms/resources/dist
. You can now symlink them into your sandbox:
cd sandboxln -s /path/to/cms/resources/dist public/vendor/statamic/cp
Do not attempt to commit any compiled code. They should already be gitignored, and will be automatically recompiled at release time.
Commit code
Now you're ready to actually write code.
If you're writing tests, you can run the test suite inside the cms
repo.
If you want to manually test or use the package, you can do it in through your sandbox. Any changes you make to the code in your cms
repo will be reflected in your sandbox app which you can see in the browser.
Once you're done, you should push your branch to Github.
git push --set-upstream origin HEAD Enumerating objects: 5, done.Counting objects: 100% (5/5), done.Delta compression using up to 8 threadsCompressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 309 bytes | 309.00 KiB/s, done.Total 3 (delta 2), reused 0 (delta 0), pack-reused 0remote: Resolving deltas: 100% (2/2), completed with 2 local objects.remote:remote: Create a pull request for 'feature/new-thing' on GitHub by visiting: remote: https://github.com/your-username/cms/pull/new/feature/new-thing remote:To https://github.com/your-username/cms.git * [new branch] HEAD -> feature/new-thingBranch 'feature/new-thing' set up to track remote branch 'feature/new-thing' from 'origin'.
Create the Pull Request
When creating a pull request that introduces a new feature or changes current behavior, please open an issue referencing your PR on the statamic/docs repo. No need to write the docs yourself. We'll take care of that for you. Any hints or bullet points are appreciated though!
In the output from pushing your branch above, it'll give you a link to create the pull request. If you missed it, no problem. Just head over to statamic/cms
and you should see a banner waiting for you.
Click through there and you'll be taken to a form where you can describe what's being contributed.
Please be as thorough as possible. Explain what's being added, what it fixes, list any relevant issues or discussions, and explain how we can test out the changes.
Cleaning Up
Once the PR is resolved, either by being merged or closed, you're free to delete the branch or even the entire fork.
If your PR was merged, you'll be mentioned in the next release's changelog where you will live in infamy. ✨
Extra Credit
If you're a frequent contributor, you may consider permanently setting up the Composer path repository.
Instead of adding repositories
key into your sandbox's composer.json
every time, you can add it to your global Composer ~/.composer/config.json
.
{ "repositories": [ { "type": "path", "url": "/path/to/cms", "canonical": false } ]}