changesets
Changesets Best Practices
Overview
Changesets is a file-based approach to versioning and changelog management. Instead of deciding versions at release time, contributors declare their changes as they work — each change gets a markdown file describing what changed and the semver bump type.
Setup
npm install -D @changesets/cli
npx changeset init
This creates a .changeset/ directory with a config.json.
Configuration
// .changeset/config.json
{
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
}
| Option | Description |
|---|---|
changelog |
Changelog generator (use @changesets/changelog-github for PR/author links) |
commit |
Auto-commit version bumps and changelog updates |
fixed |
Groups of packages that always share the same version |
linked |
Groups of packages whose versions stay in sync for matching bump types |
access |
"public" for public npm, "restricted" for private |
baseBranch |
Branch that releases are made from |
updateInternalDependencies |
How to bump dependents when a dependency changes |
ignore |
Packages to exclude from changesets |
Core Workflow
1. Add a Changeset
When you make a change worth documenting:
npx changeset
This prompts you to:
- Select which packages are affected.
- Choose the semver bump type for each (
major,minor,patch). - Write a summary of the change.
It creates a markdown file in .changeset/:
---
"@myorg/ui": minor
"@myorg/utils": patch
---
Add new DatePicker component with timezone support.
Updated date formatting utilities to handle timezone offsets.
2. Version Packages
When ready to release:
npx changeset version
This:
- Consumes all changeset files in
.changeset/. - Bumps
package.jsonversions based on the highest bump type per package. - Updates
CHANGELOG.mdfor each affected package. - Bumps internal dependents as needed.
3. Publish
npx changeset publish
Publishes all packages with new versions to npm. Run after committing the version bump changes.
Writing Good Changeset Summaries
Changeset summaries end up in your CHANGELOG.md — write them for consumers, not for yourself:
## <!-- Good: explains the impact -->
## "@myorg/button": minor
Add `loading` prop to Button component. When `true`, displays a spinner
and disables the button to prevent duplicate submissions.
## <!-- Bad: describes implementation, not impact -->
## "@myorg/button": minor
Added useState for loading state and conditional rendering of Spinner component.
Guidelines
- Write in imperative mood: "Add feature" not "Added feature".
- Focus on what changed from the consumer's perspective.
- Mention breaking changes explicitly and include migration steps.
- One changeset per logical change — don't bundle unrelated changes.
- Include code examples for API changes:
---
"@myorg/config": major
---
Change `createConfig` to accept an options object instead of positional arguments.
Before:
\`\`\`ts
createConfig("production", true, 3000);
\`\`\`
After:
\`\`\`ts
createConfig({ env: "production", debug: true, port: 3000 });
\`\`\`
Semver Conventions
| Bump | When | Examples |
|---|---|---|
patch |
Bug fixes, internal refactors, dependency updates | Fix null check, update devDependency |
minor |
New features, non-breaking additions | Add prop, export new utility, add optional parameter |
major |
Breaking changes | Remove/rename export, change function signature, drop Node version |
When in doubt between minor and patch, ask: "Would a consumer need to change their code?" If no, it's a patch.
GitHub Changelog Generator
Use @changesets/changelog-github for richer changelogs with PR links and author attribution:
npm install -D @changesets/changelog-github
// .changeset/config.json
{
"changelog": ["@changesets/changelog-github", { "repo": "grahamcrackers/my-package" }],
}
This produces changelogs like:
## 2.1.0
### Minor Changes
- Add DatePicker component ([#142](https://github.com/org/repo/pull/142)) — @grahamcrackers
CI Automation with GitHub Actions
Automated Version PRs and Publishing
Use the official changesets/action to automate the release workflow:
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: "https://registry.npmjs.org"
- run: npm ci
- uses: changesets/action@v1
with:
publish: npm run release
title: "chore: version packages"
commit: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
This action:
- Detects pending changesets on main.
- Opens a "Version Packages" PR that bumps versions and updates changelogs.
- When the PR is merged, publishes to npm automatically.
Require Changesets in PRs
Add a check that PRs include a changeset when source code changes:
# .github/workflows/changeset-check.yml
name: Changeset Check
on:
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx changeset status --since=origin/main
Empty Changesets
For PRs that don't need a version bump (docs, CI, tests), add an empty changeset:
npx changeset --empty
This creates a changeset with no packages selected, satisfying CI checks without triggering a release.
Monorepo Patterns
Fixed Versioning
Packages that should always share the same version:
{
"fixed": [["@myorg/core", "@myorg/cli", "@myorg/types"]],
}
When any package in the group bumps, all packages bump to the same version.
Linked Versioning
Packages whose versions stay in sync for the same bump type, but can diverge:
{
"linked": [["@myorg/react", "@myorg/vue", "@myorg/svelte"]],
}
If @myorg/react gets a minor bump, all linked packages that also have changesets get the same minor version.
Internal Dependencies
When package A depends on package B and B gets a major bump:
- A automatically gets a patch bump (configurable via
updateInternalDependencies). - A's dependency on B is updated to the new version.
Ignoring Packages
Exclude packages that aren't published (apps, internal tooling):
{
"ignore": ["@myorg/web-app", "@myorg/internal-scripts"],
}
Snapshot Releases
Publish pre-release versions for testing without consuming changesets:
npx changeset version --snapshot canary
npx changeset publish --tag canary
This produces versions like 0.0.0-canary-20260224 and publishes under the canary npm tag so npm install @myorg/ui still gets the stable release.
Pre-releases
For alpha/beta/rc release channels:
npx changeset pre enter beta
# Now all `changeset version` calls produce beta versions (e.g., 2.0.0-beta.0)
# Continue adding changesets and versioning as normal
npx changeset pre exit
# Exit pre-release mode and release stable versions
File Structure
.changeset/
├── config.json # configuration
├── README.md # auto-generated docs
├── fuzzy-dolphins-arrive.md # pending changeset
└── brave-tigers-dance.md # pending changeset
Changeset filenames are randomly generated — commit them as-is. They're consumed and deleted when you run changeset version.
More from grahamcrackers/skills
bulletproof-react-patterns
Bulletproof React architecture patterns for scalable, maintainable applications. Covers feature-based project structure, component patterns, state management boundaries, API layer design, error handling, security, and testing strategies. Use when structuring a React project, designing application architecture, organizing features, or when the user asks about React project structure or scalable patterns.
44react-aria-components
React Aria Components patterns for building accessible, unstyled UI with composition-based architecture. Covers component structure, styling with Tailwind and CSS, render props, collections, forms, selections, overlays, and drag-and-drop. Use when building accessible components, using react-aria-components, creating design systems, or when the user asks about React Aria, accessible UI primitives, or headless component libraries.
15clean-code-principles
Clean code principles for readable, maintainable TypeScript and React codebases. Covers naming, functions, abstraction, composition, error handling, comments, and code smells. Use when writing new code, refactoring, reviewing code quality, or when the user asks about clean code, readability, or maintainability.
10typescript-best-practices
Core TypeScript conventions for type safety, inference, and clean code. Use when writing TypeScript, reviewing TypeScript code, creating interfaces/types, or when the user asks about TypeScript patterns, conventions, or best practices.
9tanstack-query
TanStack Query v5 patterns for server state management, caching, mutations, optimistic updates, and query organization. Use when working with TanStack Query, React Query, server state, data fetching hooks, or when the user asks about caching strategies, query invalidation, or mutation patterns.
8zustand
Zustand state management patterns for React including store design, selectors, slices, middleware (immer, persist, devtools), and async actions. Use when managing client-side state, creating stores, working with Zustand, or when the user asks about global state management, store patterns, or state persistence.
7