standard-version is deprecated. Here’s where to go instead.
It bumped your version from your commits, wrote the changelog, committed and tagged, all on your own machine. The README says it’s deprecated. npm doesn’t. Below are the three ways off it, with output from a test repo where I ran the old tool and its fork side by side.
Definition
What standard-version is
standard-version is a Node CLI that cuts a release locally from your Conventional Commits. It reads the current version from package.json, picks the next semantic version from the commits since the last tag, prepends a section to CHANGELOG.md, commits both files and creates the tag. It doesn’t push and doesn’t publish. And it’s deprecated. Its README opens by saying so, recommends release-please for GitHub users, and points anyone who can’t use GitHub Actions, or needs to stay on the same tool, to a fork called commit-and-tag-version. The last release, 9.5.0, came out on 15 May 2022. The npm registry carries no deprecation flag for it, so npm install standard-version still works and prints no warning about the package itself. If it’s in your devDependencies today, it still runs. You just shouldn’t expect anyone to fix it.
Know what you’re replacing
What it did, exactly
I made a scratch repo at 1.4.2, tagged it, added a feat, a fix and a chore, and ran it:
$ npx standard-version
√ bumping version in package.json from 1.4.2 to 1.5.0
√ created CHANGELOG.md
(node:59816) [DEP0176] DeprecationWarning: fs.F_OK is deprecated, use fs.constants.F_OK instead
(Use `node --trace-deprecation ...` to show where the warning was created)
√ outputting changes to CHANGELOG.md
√ committing package.json and CHANGELOG.md
√ tagging release v1.5.0
i Run `git push --follow-tags origin main && npm publish` to publish
$ git log --oneline --decorate -1
e7b1da5 (HEAD -> main, tag: v1.5.0) chore(release): 1.5.0The changelog got a Features section and a Bug Fixes section, with commit links built from the git remote. The chore was left out. The tag is annotated, with the release commit message as its message. Nothing left my machine, and that was the whole appeal: you could look at the result, fix it, and push when you were happy.
Two behaviours worth knowing before you pick a replacement. Below 1.0.0 it treats a breaking change as a minor bump and a feat as a patch: on 0.3.1, feat!: gave 0.4.0 and feat: gave 0.3.2. And with no new commits at all, it still cuts a patch release with an empty changelog section. The replacements behave differently on both.
Closest drop-in
Path 1: commit-and-tag-version
commit-and-tag-version is the community fork the standard-version README points to. It lives at absolute-version/commit-and-tag-version, and 13.2.1 was published on 14 September 2026. Its README says you can drop it in place of standard-version, and that the old standard-version key in package.json still works. The swap:
npm uninstall standard-version
npm install --save-dev commit-and-tag-version@13.2.1{
"scripts": {
"release": "commit-and-tag-version"
}
}I tested the config claim by leaving a tagPrefix under the old standard-version key. The fork read it and tagged accordingly. The run output is the same shape, the 0.x behaviour is the same, and so is the empty patch release. What changed: patch releases get a ## heading instead of ###, with fewer blank lines (the README flags an 11.x formatting change), the changelog header now names commit-and-tag-version, and the Node 24 warning covered below went away.
Pick this if you release from your laptop on purpose, or you’re not on GitHub, or you have bumpFiles and lifecycle scripts you don’t want to rebuild. It’s the only one of the three that keeps the workflow you have.
The recommended one
Path 2: release-please
release-please is what the standard-version README recommends for GitHub users. The work moves from your terminal to CI: it keeps a release PR open with the next version and the changelog, and merging that PR is the release. The minimal workflow, adapted from the action’s README with the current major, the default token and the node release type:
name: release-please
on:
push:
branches:
- main
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v5
with:
release-type: nodeTwo differences will show up straight away. If your project is below 1.0.0, release-please turns the first breaking change into 1.0.0 and every feat into a minor, unless you turn on the two options that match what standard-version did. In manifest mode, that’s release-please-config.json:
{
"packages": {
".": {
"release-type": "node",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true
}
}
}The second is the empty release. A run with only chore or docs commits opens no release PR at all and logs “No user facing commits found since ... skipping”. Coming from standard-version, that’s a change you’ll notice, and a welcome one.
Fully automatic
Path 3: semantic-release
semantic-release is the opposite end from standard-version, and the standard-version README says as much: use it if it fits. It runs in CI on every push to a release branch, picks the version, tags, and publishes to npm and GitHub. In CI there’s no step where you look at the result first.
Three things to change on the way over. Its default plugins are the commit analyzer, the release notes generator, npm and GitHub, so there is no CHANGELOG.md unless you add @semantic-release/changelog and commit it back with @semantic-release/git. With no config it parses commits with the Angular preset, which doesn’t understand feat!, so a feat!: commit releases nothing at all. standard-version used the conventionalcommits preset, so set preset: "conventionalcommits" on both the analyzer and the notes generator. And its docs say the last release commit must be tagged in its tagFormat, which defaults to v${version}. standard-version tagged v1.2.3 by default, so old tags line up unless you changed the prefix.
If the only part you liked was the changelog, skip all three and look at git-cliff, which writes the changelog and nothing else.
Reference
Option mapping
Only options I found in each tool’s own docs or schema are listed. Everything else I left out rather than guess. release-please names are manifest config keys unless marked as CLI.
| standard-version | commit-and-tag-version | release-please | semantic-release |
|---|---|---|---|
--release-as 1.1.0 | same | a Release-As: 1.1.0 commit footer | not in its config docs |
--prerelease beta | same | prerelease versioning strategy + prerelease-type | a branch with prerelease: true (beta and alpha are in the default branches) |
--dry-run | same | --dry-run on the CLI | --dry-run (the default outside CI) |
tagPrefix / -t | same | include-v-in-tag (v on or off) | tagFormat, default v${version} |
infile (changelog path) | same | changelog-path | needs the @semantic-release/changelog plugin |
types (sections, hidden) | same | changelog-sections (type, section, hidden) | not covered here |
bumpFiles | same, plus documented Maven, Gradle, .NET, YAML, OpenAPI and Poetry files | extra-files | not in its config docs |
skip.changelog | same | skip-changelog | the changelog file is opt-in anyway |
release-please does still accept a release-as config key, but its own schema marks it deprecated and points at the commit footer instead.
Real messages
Errors you’ll hit
[DEP0176] DeprecationWarning: fs.F_OK is deprecated, use fs.constants.F_OK insteadis the line in the middle of the run above, and it printed on every standard-version 9.5.0 run on Node 24.14.0. Harmless today. It’s also what unmaintained looks like. commit-and-tag-version 13.2.1 didn’t print it.- A tag that already exists. The tool commits first and tags second, so this leaves you with a release commit and no tag:
Drop the release commit before trying again. The fork also has a√ tagging release v1.5.1 fatal: tag 'v1.5.1' already exists Command failed: git tag -a v1.5.1 -m chore(release): 1.5.1 fatal: tag 'v1.5.1' already exists--tag-forceflag for replacing a tag on purpose. Unable to load the "angular" preset. Please make sure it's installed.comes from commit-and-tag-version with--preset angularand no preset installed. Installconventional-changelog-angular(9.4.0 worked). With standard-version still installed next to it, I gotThe "angular" preset does not export a function. Maybe you are using an old version of the preset. Please upgrade.instead, because standard-version drags in the old 5.0.13 preset. Uninstall it first.- A changelog that repeats old releases. When I changed
tagPrefixtorelease-, the next dry run found no matching tags and pulled commits I’d already released in 1.5.0 into the new section. Keep the prefix you already tagged with, in whichever tool you move to.
Honesty
What this doesn’t fix
Whichever path you take, you end up where standard-version left you: a tag, a version number and a changelog that people who already use your project might read. Everyone else never finds out the release happened.
That gap is the one I built Merge & Tell for. It reads each merged pull request, the diff and not just the title, and drafts the post for each network. I read every one before it goes out. The updates page is that running on this product.
Tagged, changelogged, and still unannounced.
You were going to merge the PR anyway.