beachball
  • v3 prerelease
  • v2 stable
GitHub
  • v3 prerelease
  • v2 stable
GitHub
  • Overview

    • Getting started
    • Installation
    • Configuration
    • V3 migration guide
  • Concepts

    • Bump algorithm
    • Change files
    • Change types
    • Groups
    • CI integration
      • Auth helper
    • AI integration
    • Optimizing performance in large repos
  • CLI commands

    • Common options
    • bump
    • change
    • check
    • config
    • migrate
    • publish
    • sync
This documentation applies to beachball v3 prerelease (beachball@next). View the beachball v2 documentation or read the v3 migration guide.

Configuration

For most uses you probably do not need any specific configuration on each package within your repository. But there are a few options to customize beachball's behavior.

There are two types of configurations:

  1. repository config
  2. package config

Repository config

Most often, Beachball config is stored in beachball.config.js, but Beachball will check all the following locations under the project root directory (usually the repo root):

  • "beachball" key inside package.json
  • beachball.config.[cm]?[jt]s
  • .beachballrc (JSON) or .beachballrc.json
  • .beachballrc.[cm]?[jt]s
  • any of the above (except package.json) under a .config directory

⚠️ Beachball v3 changed the search order and removed support for YAML files and searching up from the project root to find the config file.

The beachball.config.js example below uses JSDoc type annotations to enable intellisense in some editors (these are optional).

// @ts-check
/** @type {import('beachball').BeachballConfig} */
const config = {
  disallowedChangeTypes: ['major'],
  changehint: 'Run "yarn change" to generate a change file',
  groupChanges: true,
};
module.exports = config;

Package config

Package-level configuration is currently only supported under the beachball key in package.json.

For example, suppose the repo config above is at beachball.config.js at the repo root, and there are these other files:

packages/
  foo/
    package.json
  bar/
    package.json
beachball.config.js
package.json

To change the disallowedChangeTypes for package foo, you could add the following to packages/foo/package.json:

{
  "name": "foo",
  "version": "1.0.0",
  "beachball": {
    "disallowedChangeTypes": null
  }
}

Options

For the latest full list of supported options, see the RepoOptions source.

"Applies to" indicates where the settings can be specified: repo-level config or package-level config.

OptionTypeDefaultApplies toDescription
access'public' or 'restricted'as configured for npmrepoPublish access level for scoped package names (e.g. @foo/bar) - should almost always be 'public'
branchstringsee notesrepoTarget branch; see notes
bumpDepsbooleantruerepoBump dependent packages during publish (if B is bumped, and A depends on B, also bump A)
changeFileChangeFileOptionsrepoCustomize change files, including the prompt and whether to include the git email
changehintstringrepoHint message for when change files are not detected but required
changeDirstringchangerepoDirectory where change files are stored (relative to repo root)
changelogChangelogOptionsrepoChangelog rendering and grouping options
commitMessage(options, packageInfos, bumpInfo?) => stringrepoCustomize the commit message for the change and publish commands. For publish, this is overridden by the --message CLI option (or message config value), and receives the post-bump packageInfos and bumpInfo.
concurrencynumber1repoMaximum concurrency for write operations, such as npm publish and hook calls, respecting topological order (see also npmReadConcurrency)
defaultNpmTagstring'latest'repo, packageThe default dist-tag used for NPM publish
disallowedChangeTypesstring[]repo, packageWhat change types are disallowed
fetchbooleantruerepoFetch from remote before doing diff comparisons
generateChangelogboolean | 'md' | 'json''md'repoWhether to generate CHANGELOG.md/json ('md' or 'json' to generate only that type)
getGitTag(pkg, defaultTag) => string | string[] | nullrepoGet package-specific git tag(s); return null to skip tagging a package
gitTagsbooleantruerepo, packageWhether to create git tags for published packages (eg: foo_v1.0.1). Note that getGitTag is still respected, overriding this option on a per-package basis.
groupsVersionGroupOptions[]repoBump these packages together (see details)
groupChangesbooleanfalserepoWrite multiple changes to a single change file
hooksHooksOptionsrepoHooks for custom pre/post publish actions
ignorePatternsstring[]repoIgnore changes in files matching these glob patterns (see notes)
npmReadConcurrencynumber5repoMaximum concurrency for fetching package versions from the registry (see concurrency for write operations)
packagestringrepoSpecifies which package the command relates to (overrides change detection based on git diff)
prereleasePrefixstringrepoPrerelease prefix, e.g. "beta". Note that if this is specified, packages with change type major/minor/patch will be bumped as prerelease instead.
packToPathstringrepoInstead of publishing to npm, pack packages to tgz files in numbered subfolders under this path, based on dependency tree layers (leaves first). There will also be a file versions.json at the top level with the versions of the packed packages in each layer.
publishbooleantruerepoWhether to publish to npm registry
pushbooleantruerepoWhether to push to the remote git branch
registrystringas configured for npmnpm registry to use (required if you've explicitly configured authentication for beachball)
retriesnumber3repoNumber of retries for a package publish before failing
scopestring[]repoOnly consider package paths matching these patterns (see details)
shouldPublishfalse | undefinedpackageIn most cases you should use private: true in package.json instead. This option skips the npm publish (or pack) step for this package, but it's still bumped, tagged, and gets changelog entries. Does not work to force publishing.
tagstringsee notesrepo, packagedist-tag for npm when published. Defaults to defaultNpmTag or 'latest' (npm does NOT allow publishing packages without a dist-tag).
transformTransformOptionsrepoTransformations for change files

Glob matching

Glob matching is implemented using Node's built-in glob support, which is based on minimatch with these options and supports most glob syntax.

  • Patterns are relative to the repo or project root.
  • Patterns should use forward slashes.
  • Case-sensitive matching behavior varies by OS, so it's best to ensure the patterns follow the actual casing used by files.
  • Unless otherwise noted (such as for scope), using gitignore-style negated patterns to modify previous matches is not supported.

Scoping

The scope option allows limiting which packages are considered. You can set it in the config file if it should always apply, or on the command line for a specific operation.

This option takes a list of patterns which are matched against package paths. Patterns are relative to the monorepo root and should use forward slashes. Negations are supported, similar to how gitignore works.

Example: with this config, beachball will only consider packages under packages/foo (excluding packages/foo/bar).

// in beachball.config.js or root package.json "beachball"
{
  "scope": ["packages/foo/*", "!packages/foo/bar"]
}

On the command line, this could be specified as --scope 'packages/foo/*' --scope '!packages/foo/bar' (don't forget the quotes!).

Note: if you have multiple sets of packages in the repo with different scopes, groupChanges is not supported.

Specifying the target branch and remote

The branch option is the official target branch to compare against when determining changes.

Repos which may have forks

If you have a public GitHub repo or another situation where any contributions might come from a fork, branch should use the name only (no remote) (since users can choose arbitrary names for their own remote and the official remote):

// in beachball.config.js or root package.json "beachball"
{
  "branch": "main"
}

To ensure Beachball can reliably determine which local remote name corresponds to the official remote, set repository in the repo root package.json:

// repo root package.json
{
  "repository": {
    "type": "git",
    // your repository URL here (most formats are supported)
    "url": "https://github.com/microsoft/beachball"
  }
}

Repos with a single remote (mostly Azure DevOps)

If all your contributors use branches on a single remote (as opposed to forking), you can specify the remote name as part of the branch setting. This is almost always the model used in internal Azure DevOps repos. (Do NOT use this approach for public GitHub repos.)

// in beachball.config.js or root package.json "beachball"
{
  "branch": "origin/main"
}

For safety as a fallback, it's still recommended to set repository in your repo root package.json as detailed above.

How Beachball determines the branch and remote

If branch isn't specified, the default branch name is the system default branch name (main or master).

If branch doesn't include a remote (or isn't specified), Beachball will first look for a remote matching package.json repository. If that's not found, the fallback remote is upstream if defined, origin if defined, or the first defined remote.

All the fallback logic involves git operations, so especially in large repos, it's best to give Beachball a hint using one of the approaches specified above for efficiency.

Last Updated: 8/21/26, 11:51 PM
Prev
Installation
Next
V3 migration guide