Skip to content
September 1, 2021

Ditching manual releases with Changesets

Hassle free versioning & changelog management with Changesets

The goal is simple. We want to build a library without having to manually:

  1. Update the version
  2. Update the changelog
  3. Create a new release
  4. Publish to NPM

Enter Changesets. This handy package solves all of our problems in a very elegant way.

Just tell me how it works, I'm in a rush

OK, here's how it works in a CI environment, like GitHub Actions.

  1. If your changes should update the package version, you have to include a small markdown file (changeset). No worries about the format, it's auto-generated by the Changeset CLI. Just run npx/yarn changeset, follow the prompt and you're good.
  2. Push the changes or merge the PR - essentially just get the updates to the base branch.
  3. A GitHub action will check for any number of these specific markdown files, calculate the final semver and finally open a PR with the proposed version & the generated changelog. Here's an example from the emotion repository. You read that right. If Alice made a patch change and Bob a minor & a patch one, the final version will be just a minor bump.
  4. When you're ready, merging this PR will add the changelog and will trigger another GitHub action that will publish the package in the background.

Installation & configuration

For simplicity's sake, I will use npm. Replace npx install with yarn add & npx with yarn depending on your favorite package manager.

Let's start by adding the CLI package that helps us with the version bumping, and another one to enrich our changelog with more metadata.

npm install @changesets/cli @changesets/changelog-github

Initialize the configuration..

 npx changeset init

and you will be greeted by the following message:

  Thanks for choosing changesets to help manage your versioning and publishing

  You should be set up to start using changesets now!

  info We have added a `.changeset` folder, and a couple of files to help you out:
  info - .changeset/ contains information about using changesets
  info - .changeset/config.json is our default config

All good. If you navigate inside .changeset/, you'll find the configuration file

  "$schema": "",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "linked": [],
  "access": "restricted",
  "baseBranch": "master",
  "updateInternalDependencies": "patch",
  "ignore": []

I would suggest updating only this rule for now, which will include the PR & author links next to each commit entry.

 "changelog": "@changesets/cli/changelog",
 "changelog": ["@changesets/changelog-github", {"repo": "your-name/your-repository"}],

If you're into tweaking it, here you can find detailed explanations for all the rules. Finally, let's update the npm scripts, and let Changeset take over the release.

"scripts": {
  "changeset": "changeset",
  "prerelease": "npm run build && npm run test", // optional
  "release": "changeset publish"

Setting up Workflows

Before we proceed we need two tokens:

  1. A GitHub token with repo, write:packages permissions
  2. An NPM token with write permission

After you obtain them, make sure to add them under GitHub Secrets, so that GitHub Actions can access them.

Let's create a release.yml under .github/workflows/

Code taken from changesets/action

name: Release
      - master # or main
    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
          # This makes action fetch all Git history so that Changesets can generate changelogs with the correct commits
          fetch-depth: 0
      - name: Use Node.js 14.x
        uses: actions/setup-node@v1
          version: 14.x
      - name: Install Dependencies
        run: yarn
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Ensure to have this set up under GitHub secrets
      - name: Create Release Pull Request or Publish to npm
        uses: changesets/action@master
          publish: yarn release
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Ensure to have this set up under GitHub secrets
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Ensure to have this set up under GitHub secrets

This action will create (or update) a PR every time a new changeset is added to the main branch. It will calculate the final version for the next release and prepare the changelog. When the said PR is merged, these changes will be pushed to the main branch while triggering a background job to push the package to NPM. A couple of moments later you might notice that there is a new package version if you're using the GitHub registry. That's it!

You might also want to add the changeset-bot that will notify you in PRs if there are any changesets. I find this very useful as I'm introducing new components in a design-system I'm working on, and keep forgetting to add changelog entries.

Adding a changeset

Enough of the configuration, let's do some work, and call npx changeset to version the changes. Remember, not every commit or feature needs a changeset. Updating docs or improving the test suite doesn't justify creating a new release for your end-users.

Anyway back to the terminal, let's pick our change type..

$ npx changeset
  What kind of change is this for change? (current version is 1.0.0)

Fill with the appropriate summary for the changelog..

  What kind of change is this for change? (current version is 1.0.0) 繚 patch
  Please enter a summary for this change (this will be in the changelogs). Submit empty line to open external editor
  Summary  config: introduce changesets

And call it a day. Under .changeset/ you will notice a new markdown file (its name is randomly generated), with the change-type and summary.

'change': patch
config: introduce changesets

Push the file along with the rest of the changes, and let the GitHub actions do the heavy-lifting for you. As for the markdown file, it will be deleted by our GitHub action when the entry that's referencing is added to the changelog.

Final thoughts

Changeset is godsend. Recently I had to set up a library and getting versioning/automatic publishing out of the way early was very high on my list. Thanks to Changesets the bulk of the work is invisible to me, and I only have to make the decision when to version my changes.

Previously I was using Semantic release, but I prefer keeping my commit format separate from the versioning process.

Many great libraries like MobX, XState & Formik are using it, so feel free to include it in your projects.