# Versioning & support

> What the version number promises, what the open-ended Angular peer range does and does not, how long v14 keeps getting security fixes, and what to do the day a new Angular ships.

Source: https://ngwr.dev/start/versioning  
Kind: Getting started

## Semantic versioning, and the one rule a script enforces

ngwr follows semver: a breaking change is a **major**, a new feature is a **minor**, a fix is a **patch**. The half of that worth writing down is the half that is checked rather than intended — `scripts/release-prepare.ts` refuses `--bump=minor` and `--bump=patch` outright when any commit since the last release tag carries a `!` type or a `BREAKING CHANGE:` footer. It prints every offending subject and exits non-zero, and since a release is cut only by running the Release PR workflow — whose one input is that bump — the refusal IS the release: no version is written, no changelog is generated, no pull request is opened.

```bash
# What a release actually runs. --bump is the workflow's one input,
# and this is the step that refuses it.
pnpm release:prepare --bump=minor

# Illustrative output, with one breaking commit on the branch:
#
#   Refusing --bump=minor: 1 breaking commit(s) since v14.0.0.
#     feat(select)!: clearable is opt-in
#   A `!` type or a `BREAKING CHANGE:` footer requires --bump=major.
#
# Exit status 1. No version written, no changelog, no release PR.
```

**It exists because the opposite shipped.** v12.2.0 was a _minor_ that carried a `BREAKING CHANGES` heading in its own changelog — `select: clearable is opt-in` — and an app on `^12.1.0` received it on the next install with nothing said. The bump was a value someone picked from a dropdown, and nothing connected it to the commits it was about to publish. That connection is now a step in the release, which is the only place it can live: a review can miss it, a convention can be forgotten, and a changelog that contradicts its own version number is written after the decision has already been made.

What the guard reads, and therefore what it cannot see.

It reads commit metadata — a ! on the type, or a BREAKING CHANGE: footer. A break nobody labelled as one is invisible to it, and no script can close that: whether a behaviour change breaks you is a fact about your code. Conventional-commit subjects are enforced by commitlint on every commit and on every pull-request title, which is what gives the guard something to read; judging what counts as breaking is still a person's job. And the direction it does not run is the harmless one — a major carrying nothing breaking is legal and happens.

Two smaller rules complete it. **`rc` is exempt**, and has to be: a release candidate is where a major is staged, and it publishes to the `next` dist-tag rather than to `latest`, so nobody receives it by upgrading. And **“a patch is only fixes” is a convention, not a gate** — 10.2.1 shipped a Features section. Nothing in that patch broke anything, which is why the guard is aimed where it is; but if you want to know exactly what a version contained, the changelog for it is generated from the commits rather than written, so it tells you.

## What a major is allowed to break

Three surfaces, not one, and the two that are not TypeScript are the ones people are surprised by. Components ship `ViewEncapsulation.None`, so their `.wr-*` BEM classes are reachable from your stylesheet and are treated as API; the `--wr-*` custom properties are API for the same reason. All three change only in a major, and the middle column below is the part to read.

| Surface | Covered | How you check one |
| --- | --- | --- |
| Anything with a docs page | Yes | Every component, directive, pipe, service, util and validator under /reference, and every provideWr*() function. Their API tables are compared against the library on every pull request — names, types and defaults alike — so most drift between a page and the source is a red build rather than something you find at runtime. |
| .wr-* class names | Yes | Components ship ViewEncapsulation.None, so the BEM classes are styleable and are treated as API. They are also what the test harnesses read. Inspect the element — what you see is what a major has to keep. |
| --wr-* custom properties | Yes | The token layer, plus the per-component hooks listed in the CSS variables section of each component page. Both halves are gated: check:tokens fails a token in the theme layer that nothing paints with, and check:css-vars regenerates every per-component list from the stylesheets, so the hooks a page names are the hooks the CSS reads. |
| Marked @internal | No | The marker survives into the shipped types, so it is greppable: grep -rn "@internal" node_modules/ngwr/types. Injection tokens a component uses to talk to its own parent live here — WR_SELECT, WR_TABS, WR_CHECKBOX_GROUP, WR_RADIO_GROUP, WR_COLLAPSE_GROUP, WR_BUTTON_GROUP, WR_CAROUSEL, WR_ICONS, WR_I18N_CONFIG, WR_OVERLAY, WR_OVERLAY_CONTAINER. Provide the documented provideWr*() instead. |
| Exported, undocumented, unmarked | Not yet | A real gap rather than a category: wrAppendOverlayClose, wrMirrorOffsets, wrPresentAsSheet and WrOutsideClick from ngwr/overlay, useConfigValue, useFormFieldAria, squirclePath, wrContrastFor, wrIntentTokens, isSafeCssValue, and a handful of WR_* context tokens such as WR_STEPPER. They import, they work, and nothing on this site describes what they promise. Treat them as unsupported until a page exists — and open an issue naming the one you want, because that is what turns a hole into a decision. |

The last row is the honest one and it is worth stating plainly rather than leaving to a reader with a `.d.ts` open: **being exported is not the same as being supported.** An entry point's `public-api.ts` exports a few helpers that exist because a component needed them, and a symbol like that carries no promise until something describes what it promises. The machine-readable half of this is the `@internal` marker, which survives into the published types — so the question “may I use this?” has a grep-able answer for one category and, for now, a judgement call for the other.

```bash
# Everything on this page is a fact about the repository. Read it there.
cat node_modules/ngwr/package.json | jq .peerDependencies   # the ranges
grep -rn "@internal" node_modules/ngwr/types                # what is not API
npm view ngwr time                                          # every release, dated
npm view ngwr dist-tags                                     # latest, and any rc on `next`
```

## How long a line keeps getting fixes

Two lines at a time: the current major in full, the one before it for mechanical security fixes only. The table below is the same one `SECURITY.md` carries, and neither copy is maintained by hand — `release:prepare` rewrites it from the version being cut, after the hand-maintained one spent two majors naming 10.x as current against a shipped 12.2.0.

| Line | Support | What that means |
| --- | --- | --- |
| `14.x` | Full | Every security fix, and every other fix. This is the line releases are cut from. |
| `13.x` | Limited | Security fixes only, and only where the fix is mechanical — no new inputs, no narrowed types, nothing that changes what already-accepted input renders. Where the only correct fix is not mechanical the advisory says so and the answer is to upgrade. This row ends when v15 ships. |
| `< 13.0` | None | Unsupported. Report against any version you can reproduce on — working out which majors are affected is triage’s job, not yours — but the fix will land on a supported line. |

Read the middle row against the cadence, not against a calendar.

“Until the next major” is a duration set by the release schedule, and on this project that has meant weeks. Eight majors shipped between 2026-06-12 (v7.0.0) and 2026-09-04 (v14.0.0). If your plan is to sit a major behind and take security fixes, that plan has to survive the previous line going unsupported the day the next one ships — which is a reason to upgrade on the majors, not a reason to expect a longer window than the table states.

Reporting is private: [GitHub's private vulnerability reporting](https://github.com/thekhegay/ngwr/security/advisories/new) or email, never a public issue. The targets — 72 hours to acknowledge, 7 days to triage, 30 days to a fix or workaround for high and critical — are in [SECURITY.md](https://github.com/thekhegay/ngwr/blob/main/SECURITY.md), with the scope: the published package and its bundled assets, not this site and not the dependencies, which go upstream.

## The Angular range — a real floor and an open ceiling

Every Angular peer is declared `>=22.0.0`, and the two halves of that range mean very different things. **The floor is real and it is enforced by the compiled output**, not by the range — though by a minority of it: of 654 partially-compiled declarations, 25 record `minVersion: 22.0.0` (the service declarations, spread across 22 files), and one unreadable declaration is enough to fail the build. **The ceiling is absent, and absent is not a promise.** It means your package manager will not stop you from installing ngwr next to an Angular that did not exist when the release was cut — nothing more.

**Below the floor, the install succeeds and the build is what fails.** npm, pnpm and yarn all treat an unsatisfied peer as a warning, so adding ngwr to an Angular 21 project prints “Issues with peer dependencies found” and exits 0. The failure surfaces later, in the linker, as `Unsupported enum value for [object Object]` pointing into a `fesm2022` bundle — a message that never mentions a version. Read the peer warning at install time; it is the only place the real reason is printed.

**Above the ceiling, nobody knows yet, and the honest answer is a procedure rather than a range.** When a new Angular major lands, ngwr has not been built or tested against it — the gates run on the Angular the repository is pinned to. So: upgrade Angular on a branch, run your own suite, and expect a supporting ngwr release rather than a widened range, because the fix for a framework break is code. Nothing about the open range makes an untested combination work, and nothing about it will warn you that you are in one. [The gates page](https://ngwr.dev/start/quality) lists what runs and what each gate cannot see; there is one maintainer and no dated commitment, which is worth weighing before you plan an upgrade around a release that has not been announced.

**TypeScript and Node are decided by your Angular, not by ngwr.** The package declares no `engines` field and no TypeScript peer at all: it ships pre-compiled bundles and `.d.ts` files, so the compiler version that matters is the one Angular's own peer range names — `typescript >=6.0 <6.1` for Angular 22 — and the Node version is whatever your Angular CLI accepts. That is a deliberate absence rather than a missing field: a second range here could only ever contradict the first one.

## The cadence, stated rather than inferred

It is fast, and reading it out of a changelog gives a worse impression than reading it here. **Eight majors shipped between 2026-06-12 (v7.0.0) and 2026-09-04 (v14.0.0)** — under a fortnight apart on average. That was a library settling its vocabulary, its entry-point layout and its palette before it had an install base to protect: two of those majors broke nothing but painted colour, and the largest single category across the rest is renames — `closeable` to `closable`, `totalItems` to `total`, `compact` / `normal` to `sm` / `md`. The table on [Quality](https://ngwr.dev/start/quality) has what each one actually cost. Useful to know, and not a promise about next quarter.

Three things follow, and they are what a team can actually act on. **One: a caret range on a major is a safe range now**, which it demonstrably was not on v12 — that is what the guard at the top of this page buys, and it is the whole reason it is worth stating publicly instead of leaving you to infer a policy from a changelog. **Two: budget for reading a migration on each major, not for a rewrite** — [the migration guide](https://ngwr.dev/start/migration) covers every break back to v6, and `ng update ngwr@14` rewrites the half a codemod can honestly repair and reports the half it cannot. **Three: check rather than trust this paragraph**, because a page written today describes the releases that existed today.

```bash
# Pin the major and let the minors in. That is a safe range now — the guard
# above is what makes it one — and it was not on v12, where 12.2.0 was a minor
# carrying a breaking change.
npm pkg set dependencies.ngwr="^14.0.0"

# Then read what a range would actually have picked up, before you widen it.
npm view ngwr versions --json
```

What none of this buys: there is no commercial support, no SLA and no contract — [Quality](https://ngwr.dev/start/quality) says so in the same words. The library is MIT and every gate runs in the public repository, so a fork is a real option; that is the fallback the licence gives you, not a plan anyone is offering to run for you.

## See also

- [Quality](https://ngwr.dev/start/quality) — What gates a release, what the gates cannot see, and what the project does not buy you.
- [Migration guide](https://ngwr.dev/start/migration) — Every breaking change back to v6, and which half of each major the codemod rewrites.
- [Installation](https://ngwr.dev/start/installation) — The peer list in full, what ships in the tarball, and what a per-component style entry costs.
