Dev Encyclopedia
ArticlesToolsContactAbout

Get notified when new content drops

No spam. Just new articles, tools, and updates straight to your inbox.

Dev Encyclopedia

A reference for builders

Dev.to
Discord
WhatsApp Channel
daily.dev
Hashnode
X

Content

  • Articles
  • Tools
  • About
  • Contact

Connect

  • support@devencyclopedia.com
  • RSS Feed

Legal

  • Privacy Policy
  • Terms of Service
  • Disclaimer

© 2026 Dev Encyclopedia

Back to top ↑
  1. Home
  2. /
  3. Tools
  4. /
  5. CommitCheck
Free · Live

Is your commit message valid?

CommitCheck validates Conventional Commits format in real time, no Commitlint, no setup, no account.

Zeeshan Tofiq

Zeeshan Tofiq

Full Stack Developer

How CommitCheck works

CommitCheck is a parser, not a spell checker. It takes your message apart the same way a release tool does, then reports every rule the parts break.

  1. 1

    You type or paste a commit message

    Validation runs on every keystroke, with no debounce and no network call. You can paste a full multi-line message: body and footer lines are preserved, but the rules below are applied to the header (the first line), because that is the line release tooling parses.

  2. 2

    The header is split at the first colon

    Everything before the first colon becomes the prefix (type, optional scope, optional breaking-change marker). Everything after it becomes the description. If there is no colon at all, validation stops immediately with a single error, because nothing else can be determined.

  3. 3

    The prefix is broken into type, scope, and breaking flag

    A trailing ! sets the breaking flag. A pair of parentheses is extracted as the scope. Whatever remains at the front is the type. This is why fix(api)!: parses cleanly into type fix, scope api, breaking true.

  4. 4

    Each part is checked against the spec

    The type must be one of the 11 lowercase types listed below. The scope, if present, must be non-empty and contain no spaces. The description must exist, and it is checked for capitalisation, a trailing period, and length.

  5. 5

    Unknown types get a suggestion

    Common near-misses are mapped to the right type, so feature suggests feat, bugfix and hotfix suggest fix, and documentation suggests docs. If there is no known mapping, a prefix match is attempted before the tool gives up.

  6. 6

    Results are split into errors and warnings

    Errors mean the message is not a valid Conventional Commit. Warnings mean it parses but breaks a common convention. When there are zero errors, the parsed breakdown (type, scope, breaking, description) is displayed so you can confirm the tool read your intent correctly.

What the validation result means

CommitCheck returns one of three states. The difference between the amber state and the red state matters: one will still drive your changelog, the other will be skipped entirely by release tooling.

Valid Conventional Commit: Zero errors, zero warnings

The message parses cleanly and follows every convention the tool checks. The parsed breakdown appears underneath so you can confirm the type, scope, breaking flag, and description are what you intended. This is the state to aim for on any commit that will reach your main branch.

feat(auth): add refresh token rotation

Type: feat  ·  Scope: auth  ·  Breaking: No
Description: add refresh token rotation
Valid, with style suggestions: Zero errors, one or more warnings

The message parses, and semantic-release or release-please will happily read it. But it breaks a convention that most teams follow, such as a capitalised description or a trailing period. Commitlint's config-conventional preset turns several of these into hard errors, so treat warnings as things to fix rather than opinions to ignore.

feat: Add login page.
#          ^ capitalised    ^ trailing period
# Both are warnings here, both are errors under commitlint defaults.
Invalid commit message: One or more errors

At least one rule makes the message unparseable as a Conventional Commit. The parsed breakdown is hidden, because there is nothing reliable to show. In a repo with automated releases, a commit in this state is silently dropped from the changelog and contributes nothing to the version bump.

feature: add login page
# 'feature' is not a valid type. Did you mean 'feat'?

fix:handle expired tokens
# Missing space after the colon.

Every rule CommitCheck applies

Each row is a real check with the exact input that triggers it. Paste any example into the tool above to see the message it produces.

CheckSeverityExample that triggers it
No colon in the headerErroradd login page
No space after the colonErrorfix:handle null user
Type is not in the valid listErrorfeature: add login page
Type is not lowercaseErrorFeat: add login page
Scope parentheses are emptyErrorfix(): handle null user
Scope contains a spaceErrorfix(auth service): handle null
Scope parenthesis never closesErrorfix(auth: handle null user
Characters after the closing parenthesisErrorfix(auth)x: handle null user
Description is emptyErrorfeat:
Description starts with a capitalWarningfeat: Add login page
Description ends with a periodWarningfeat: add login page.
Description longer than 100 charactersWarningfeat: add a login page and also refactor the entire session layer while we are in here ...
Scope is not lowercaseWarningfeat(Auth): add login page

Conventional Commits syntax reference

A full Conventional Commit has up to three parts: a required header, an optional body, and optional footers. Blank lines separate them, and that separation is what tools use to tell them apart.

Anatomy of a commit message
type(scope)!: description
 |     |    |       |
 |     |    |       +-- short summary, lowercase, no trailing period
 |     |    +---------- optional: marks a breaking change
 |     +--------------- optional: the area of the codebase touched
 +--------------------- required: one of the 11 types below

<blank line>
Optional body. Free-form prose explaining why the change was
made. Can span as many lines and paragraphs as you need.

<blank line>
BREAKING CHANGE: description of what consumers must change
Refs: #482
Reviewed-by: Zeeshan Tofiq
Valid header forms (all pass CommitCheck)
feat: add dark mode toggle
feat(ui): add dark mode toggle
feat(ui)!: replace the theme API with CSS variables
fix(api-gateway): retry once on upstream 502
chore(deps): bump next from 15.4.0 to 16.0.0
revert: "feat(ui): add dark mode toggle"
perf(db): replace N+1 order lookup with a single join
docs: document the NEXT_PUBLIC_ prefix rules
Edge cases worth knowing
# Colons inside the description are fine: only the FIRST colon splits.
fix(parser): handle the case where key: value has no space

# Hyphens and dots are allowed inside a scope, spaces are not.
fix(auth-service): renew the session cookie      # valid
fix(auth service): renew the session cookie      # error

# The ! goes AFTER the scope, never inside or before it.
feat(api)!: drop the v1 endpoints                # valid
feat!(api): drop the v1 endpoints                # error

# A BREAKING CHANGE footer works without the ! marker.
feat: move config to a single file

BREAKING CHANGE: .apprc is no longer read. Move all keys
into app.config.ts before upgrading.

The spec allows BREAKING-CHANGE (hyphenated) as a synonym for BREAKING CHANGE in footers, because Git trailers do not permit spaces in keys. Both are recognised by release tooling.

Valid commit types

Only feat and fix are defined by the spec itself. The other nine come from the widely used Angular convention, and they are what commitlint accepts out of the box.

featA new feature
fixA bug fix
docsDocumentation only changes
styleFormatting and whitespace, no logic change
refactorCode change that is neither a fix nor a feature
testAdding or correcting tests
choreMaintenance: deps, config, tooling
perfA code change that improves performance
ciChanges to CI configuration files and scripts
buildChanges to the build system or dependencies
revertReverts a previous commit

Only feat and fix bump a version by default. Everything else is recorded in history without changing the release number, unless it carries a breaking-change marker.

How to mark breaking changes

Two methods, both valid. The ! shorthand is more visible in git log; the footer works better for multi-line explanations.

Method 1: ! shorthand (recommended)

feat!: remove deprecated /auth/login endpoint
fix(api)!: change token format from JWT to opaque tokens

Method 2: BREAKING CHANGE footer (more detail)

feat: replace /auth/login with OAuth flow

BREAKING CHANGE: The /auth/login endpoint has been removed.
Clients must use /oauth/authorize instead.

Either method triggers a major version bump in semantic-release and release-please. You can use both together when you want the marker visible in git log --oneline and the explanation available to changelog readers.

When to use CommitCheck

Each row maps a real situation to what you should paste into the tool and what part of the result to read first.

SituationWhat to pasteWhat to read
Your pre-commit hook rejected a commitThe exact message commitlint refusedThe error list, which names the failing rule in plain language
Writing the commit that triggers a major releasefeat(api)!: drop the v1 endpointsThe parsed breakdown, confirming Breaking reads Yes
Reviewing a squash-merge PR titleThe PR title, which becomes the squashed commitWhether the type is valid, since an invalid title skips the changelog
Teaching the format to a new teammateThe built-in bad type and no space examplesThe suggestion text, which shows feature maps to feat
Auditing an existing repo before adopting semantic-releaseMessages from git log --format=%s -n 50, one at a timeThe count of invalid messages, which tells you the migration cost
Deciding whether a scope helpsThe same message with and without a scopeThe Scope field, plus how the header reads at a glance

Why use Conventional Commits?

  • Automated changelogs. Tools like release-please, semantic-release, and conventional-changelog read your commit history to generate CHANGELOG.md entries automatically.
  • Semantic version bumps. feat commits bump the minor version. fix commits bump the patch. Breaking changes (! or BREAKING CHANGE footer) bump the major version, automatically.
  • Readable git history. A consistent format makes it easy to scan what changed and why, especially when narrowing down a bug introduction with git bisect.
  • Tooling compatibility. GitHub Copilot suggests it by default. Changesets, nx, turborepo, and most modern monorepo tools understand it natively.
  • Cheaper release reviews. Grouping by type means a release reviewer can read every feat and fix first, then skim the chore and ci entries, instead of reading 200 unstructured subject lines.

Frequently Asked Questions

What is Conventional Commits?

A specification for structuring Git commit messages. Format: type(scope): description.

  • Enables automated changelogs: tools read your commit history to generate CHANGELOG.md automatically.
  • Drives semantic version bumps: feat = minor, fix = patch, feat! = major.
  • Tools that depend on it: semantic-release, release-please, conventional-changelog, Changesets.
How is CommitCheck different from Commitlint?
CommitCheckCommitlint
SetupZero (browser tool)Install + config file + Husky hook
Use caseQuick validation, learning the formatTeam-wide enforcement in CI
Works offline?Yes, all in-browserYes, local CLI
Blocks commits?NoYes, pre-commit hook

Use CommitCheck to learn and validate. Use Commitlint to enforce the format for every developer on the team.

Is the scope required?

No. Scope is optional. Both of these are valid:

bash
feat: add login page           # no scope
feat(auth): add login page     # with scope

Use scope when it helps readers understand which part of the codebase changed.

How do I mark a breaking change?
bash
# Method 1: ! shorthand (recommended, visible in git log)
feat!: remove deprecated /auth/login endpoint

# Method 2: BREAKING CHANGE footer (better for detailed explanation)
feat: replace authentication with OAuth

BREAKING CHANGE: /auth/login has been removed. Use /oauth/authorize.

ℹ Info

Either method triggers a major version bump in semantic-release and release-please.

What commit types are valid?
TypeWhen to use
featNew feature
fixBug fix
docsDocumentation only
styleFormatting, no logic change
refactorCode restructure, not a fix or feature
testAdding or correcting tests
choreMaintenance (deps, config, tooling)
perfPerformance improvement
ciCI config changes
buildBuild system or dependency changes
revertReverts a previous commit
Does CommitCheck work offline?

Yes. All validation runs entirely in your browser, and there are no API calls. Once the page loads, it works without an internet connection. Nothing you paste is sent anywhere or stored.

Does CommitCheck validate the body and footer, or only the first line?

You can paste a full multi-line message, but the rules are applied to the header (the first line). That matches how release tooling works: semantic-release and release-please parse the header for type, scope, and breaking marker, and only scan the remaining lines for footers such as BREAKING CHANGE:.

bash
feat(auth): add refresh token rotation   # <- validated

Sessions now rotate the refresh token on every use.   # body, free-form

BREAKING CHANGE: stored refresh tokens are invalidated.   # footer

💡 Tip

Keep the header under about 72 characters so git log --oneline stays readable in a terminal, and put the detail in the body.

My team uses custom types like wip or deps. Will they pass?

No. CommitCheck validates against the 11 types listed above, which is what commitlint's config-conventional preset accepts by default. A custom type is reported as an error, with a suggested replacement when one is obvious.

If your team genuinely needs extra types, add them to your own commitlint config rather than relying on convention alone, so CI enforces the same list everyone agreed on:

javascript
// commitlint.config.js
export default {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-enum": [
      2,
      "always",
      ["feat", "fix", "docs", "style", "refactor", "test",
       "chore", "perf", "ci", "build", "revert", "deps"],
    ],
  },
};

Be aware that release tools only bump versions for feat, fix, and breaking changes unless you configure them otherwise. A custom type is recorded in history but is invisible to your changelog by default.

How do I check commits I already pushed?

Print the subject lines from your history and paste them into the tool one at a time to get a feel for how much of your history is already compliant:

bash
# Last 50 commit subjects, one per line
git log --format=%s -n 50

# Only subjects that already look conventional
git log --format=%s -n 200 | grep -E '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\([^)]+\))?!?: '

⚠ Warning

Do not rewrite pushed history just to fix commit messages on a shared branch. Adopt the format going forward and let the changelog start from the next release.

Related reading

Guide

Husky + Prettier + lint-staged Setup for Next.js

Enforce commit format on every commit: Husky pre-commit hook with Commitlint for team-wide enforcement.

Guide

npm Scripts You're Probably Not Using

pre/post hooks and git lifecycle scripts: the workflow that makes conventional commits actionable.

Zeeshan Tofiq

Zeeshan Tofiq

Full Stack Developer

Full stack developer with over 6 years of experience building production applications. Writes practical guides on JavaScript, TypeScript, React, Node.js, and cloud infrastructure. Focused on helping developers solve real problems with clean, maintainable code.

Enjoyed this article?

Get practical dev guides, tool updates, and new articles delivered straight to your inbox. No spam, unsubscribe anytime.