Skip to content
  1. Blog

Using Composer with WordPress

Scott Walkinshaw Scott Walkinshaw on

This post was last updated 2023-02-20

This post will give you an introduction to Composer and how to manage WordPress core, plugins, and even themes with it. If you already know what Composer is and how to use it and just want to see how to integrate it with WordPress, you can skip straight to the The Solution section below.

Bedrock, our WordPress boilerplate, uses Composer for dependency management.

Intro to Composer

Composer is a dependency manager for PHP that has been gaining steam lately. Your first question is most likely “what is a dependency manager and why do I need one?”. Almost any code you write probably ends up depending on 3rd party libraries. All of these libraries (projects, frameworks, files, etc) become dependencies of your project. Composer lets you declare the dependencies for a project and it will install and manage them.

If you’ve used npm for node.js, Bundler for Ruby, or pip for Python, then you’ve already used a dependency manager.

All these tools deal with packages. Every dependency is also a package. What constitutes a package? It can be a local file, local folder, remote zip, local Git repository, remote Git repository, GitHub repository, etc. Most dependency managers also include a global registry of available packages. For Composer, this is Packagist. To get an idea of what kind of packages are available, just browse through Packagist.

I’ll do my best to explain some Composer concepts throughout this post, but the official Composer docs are quite accessible as well. It’s recommended to read them to get a full understanding of Composer.


Installing Composer is pretty simple with one caveat: It requires PHP 7.2.5. Follow the installation instructions to get started. I suggest installing Composer globally and then you can just use the composer command from anywhere.


Seeing Composer in action usually solves most of the confusion you might have after reading about dependencies and packages. Let’s go through the most basic example of installing a single dependency to an empty project.

In this case our project is just an empty folder to start with:

$ mkdir composer-example
$ cd composer-example

For Composer, you declare your dependencies in a composer.json file. Create one with the following JSON:

  "require": {
    "guzzlehttp/guzzle": "^7.0"

For this example we’re only declaring one dependency: Guzzle, the popular PHP HTTP client. Note that every package name includes its vendor name as well for namespacing purposes. In this case they’re both the same.

The next thing to notice is that we also declare what version of the Guzzle package we want. We’re using the latest ^7.0 which can be found at

Only thing left to do is to actually install our dependencies:

$ composer install

And you’ll get the following output:

Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing guzzlehttp/guzzle (7.5.0): Extracting archive

Writing lock file
Generating autoload files

Let’s list out some folders to see what actually happened:

$ ls
composer.json composer.lock vendor
$ ls vendor
autoload.php composer twig

We can see that Composer created a vendor/ directory that contains the twig package.

You might be wondering about the new composer.lock file that was generated. The official Composer docs explain it nicely:

After installing the dependencies, Composer writes the list of the exact versions it installed into a composer.lock file. This locks the project to those specific versions.


You might still be wondering what’s the point in using Composer and going through all the work above. Composer (and any dependency manager for that matter) has many advantages:

  • your dependencies are explicitly declared in a single place
  • installing and updating is handled by the tool
  • your project is locked onto specific versions
  • you don’t need to include the actual 3rd party code in your version control repository

The last one is huge. Without a dependency manager, you’re stuck doing one of two things:

  • adding the entirety of a 3rd party library into your VCS repo
  • using something like Git submodules

This means that when you’re using Composer, you check the following files into your repository:

  • composer.json
  • composer.lock

That’s it. Add vendor/ to your .gitignore and let Composer handle it. Now whenever wants to setup your project, they just run the standard git clone followed by composer install.

Integrating Composer with WordPress

Finally we’re ready to talk about how you use Composer with WordPress. When you think about a typical WP site, the easiest example of dependencies are plugins. But there’s something more fundamental to realize: WordPress itself is a dependency.

If you think about it, you’re creating a site, or application, that depends on WordPress. The WordPress core is a 3rd party library that is required for your application to work. This is difficult to understand at first due to how WordPress sites are usually structured:


You’re probably familiar with the above folder structure. The WordPress core files are in your project’s root directory. wp-content/ actually contains all your application specific code like your theme and plugins.

Thankfully most people realized this wasn’t a good idea and putting WordPress in its own subdirectory has become a common practice. It might look like this:


A further improvement was including WordPress as a Git submodule. WordPress-Skeleton is a popular example of this. Your folder structure looks the same as above, but at least your wp/ folder isn’t part of your main repository since it’s a separate submodule.

Since you’re reading an article about Composer, you’ve probably realized that Git submodules aren’t the proper way to deal with dependencies either.

The good thing about the above structure becoming popular is that it’s not much different with Composer. In fact, your WP folder structure can be identical. It’s just a matter of using Composer to install WordPress to wp/ rather than using a Git submodule.

How To

There’s a few issues to deal with in order to get Composer to install WordPress to wp/ and achieve the structure we had above.

As you saw in our simple Guzzle example, Composer installs all dependencies to vendor/. Unfortunately, this won’t work for WordPress since it needs to be our root directory (WP will only look for wp-config.php in its own directory or one directory up limiting its location).

A regular Composer package can only be installed in vendor/. You can’t pick and choose where you want each package installed to in your project.

Of course there’s a workaround otherwise this post wouldn’t exist. Composer by default doesn’t let you override a package’s install path, but Composer has plugin packages that can define their own install paths which is exactly what we need.

Here’s a WordPress Core Installer package which would let us configure the install path like this:

"extra": {
  "wordpress-install-dir": "wp"

Problem solved right? We can just implement the wordpress-core-installer package and we’re good? Unfortunately the biggest issue remains:

WordPress itself doesn’t have a composer.json file so it’s not a Composer package yet.

There’s been a very long discussion about adding a composer.json file to WordPress but it hasn’t happened yet and isn’t likely to anytime soon.

The Solution

The best solution right now is to use a “fork” of WordPress with composer.json already added. is our Composer package with WordPress.

Now we can finally get to the working composer.json file that we need:

  "require": {
    "php": ">=8.0",
    "roots/wordpress": "6.1.1"
  "extra": {
    "wordpress-install-dir": "wp"

That’s it. We just require roots/wordpress with the version of WordPress that we want and then tell Composer we want WordPress installed to wp.

Installing WordPress with Composer

Now that we have our working composer.json file, let’s actually install WordPress. Once again, run composer install. Here’s what you should see:

Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing roots/wordpress-core-installer (1.100.0): Extracting archive
  - Installing roots/wordpress-no-content (6.1.1): Extracting archive
  - Installing roots/wordpress (6.1.1)

Writing lock file
Generating autoload files

And our resulting directory looks like:

$ ls
composer.json composer.lock vendor wp

At this point, we need to follow the instructions about giving WP its own subdirectory found here. Once that’s done, our project should look like this:


And we’re back to what we originally wanted. Only 1600 words later.

More on Packages

I said earlier that all packages are installed to vendor/ unless they use another plugin package to allow overriding. Before we used one specifically for WordPress core, but there’s another more general one which defines more package types and install paths: composer-installers.

composer-installers lets a package specify its type and a custom install location. They have a few types already included that we care about:

  • wordpress-plugin => wp-content/plugins/{$name}/
  • wordpress-theme => wp-content/themes/{$name}/
  • wordpress-muplugin => wp-content/mu-plugins/{$name}/

So any package with a type of wordpress-plugin will get installed to wp-content/plugins/{$name}/ by default.

Installing WordPress Plugins with Composer

So we’ve got WordPress installed and now we want to add some plugins. Thanks to composer-installers we can install them in the proper location right?

Yes, but we’re limited to plugins that have a composer.json, require composer-installers AND have their package type set correctly. Obviously there aren’t many plugins that actually do this yet.

WordPress Packagist

Fortunately, the awesome people at Outlandish have created a repository of WordPress plugins called WordPress Packagist.

This site provides a mirror of the WordPress plugin directory as a Composer repository.

Basically, they mirror every WordPress plugin and add a composer.json file that requires composer-installers and sets their package type to wordpress-plugin.

Their instructions are simple too:

  1. Add the repository to your composer.json
  2. Add the desired plugins and themes to your requirements using wpackagist-plugin or wpackagist-theme as the vendor name.
  3. Run $ composer update
  4. Plugins are installed to wp-content/plugins/ or wp-content/themes/

Our composer.json will look like this now after adding some plugins:

  "repositories": [
      "type": "composer",
      "url": ""
  "require": {
    "php": ">=8.0",
    "roots/wordpress": "6.1.1",
    "wpackagist-plugin/akismet": "5.0.2",
    "wpackagist-plugin/turn-comments-off": "1.2.0"
  "extra": {
    "wordpress-install-dir": "wp"

It took a lot of work to get here, but if you look at the composer.json above, it’s actually pretty simple and self explanatory.

Take a look at Bedrock for a stable starting point for WordPress projects with Composer.

About the author

Scott Walkinshaw

Scott Walkinshaw is a full-stack web developer who's trying to drag WordPress into 2024 and help people modernize their development workflow. He lives in Toronto and likes sports almost as much as coding.

Subscribe for updates

Join over 8,000 subscribers on our newsletter to get the latest Roots updates and tips on building better WordPress sites

Looking for WordPress plugin recommendations, the newest modern WordPress projects, and general web development tips and articles?