Skip to content

Changelog Generation

Learn how to customize Versu's changelog generation.

Basic Configuration

Versu uses conventional-changelog-writer under the hood to generate changelogs based on your commit history. Changelog generation will pick up all commits since the last release and format them according to the rules defined in your configuration.

You can customize both the generation of the root changelog and the module changelogs (if you're using a monorepo setup) by providing specific configurations for each.

javascript
export default {
  // Other configuration options
  changelog: {
    // Changelog configuration options
    root: {
      // Root changelog configuration (optional)
      context: { /* */ }, // Context<Commit> (1)
      options: { /* */ }, // Options<Commit> (2)
    },
    module: {
      // Module changelog configuration (optional)
      context: { /* */ }, // Context<Commit> (1)
      options: { /* */ }, // Options<Commit> (2)
    },
  },
};

1 The context property allows you to provide additional information about the commits being processed. This can include details about the commit types, scopes, and any other relevant metadata that can be used to customize the changelog output.

2 The options property allows you to specify various options for how the changelog is generated. This can include settings for how commit messages are formatted, how sections are organized, and any other customization options provided by the underlying changelog generation library.

Grouping Commits

As an example lets imagine you want to group commits in your changelog by their type (e.g., features, bug fixes, etc.). You can achieve this by customizing the context and options properties in your changelog configuration.

javascript
function commitGroupsSort(a, b) {
    const order = [
        "✨ Features",
        "🐛 Bug Fixes",
        "📝 Documentation",
        "⚡️ Performance",
        "♻️ Refactor",
        "🎨 Styling",
        "✅ Testing",
        "🤖 Build",
        "🔁 CI",
        "📦 Chores",
        "⏪ Revert",
        "❓ Other",
    ];
    return order.indexOf(a.title || "") - order.indexOf(b.title || "");
};

function transform(commit, context) {
    const typeMap = {
        feat: "✨ Features",
        fix: "🐛 Bug Fixes",
        doc: "📝 Documentation",
        perf: "⚡️ Performance",
        refactor: "♻️ Refactor",
        style: "🎨 Styling",
        test: "✅ Testing",
        build: "🤖 Build",
        ci: "🔁 CI",
        chore: "📦 Chores",
        revert: "⏪ Revert",
    };

    // 1. Create a shallow copy so we don't mutate the original
    const modifiedCommit = {...commit};

    // 2. Skip commits without a type (e.g., merge commits)
    if (!modifiedCommit.type) return null;

    /**
     * 3. Map the type to a human-friendly title with an emoji,
     * defaulting to "Other" if unknown
     */
    modifiedCommit.type = typeMap[modifiedCommit.type] || "❓ Other";

    return modifiedCommit;
};

const options = {
    // How to group the commits (mapped in transform)
    groupBy: "type",
    // The order sections appear in the changelog
    commitGroupsSort,
    // Transform raw commit data into the new format
    transform,
};

export default {
  // Other configuration options
  changelog: {
    root: {
      options,
    },
    module: {
      options,
    },
  },
};

Changelog Format

Output format is fully customizable by using handlebars templates. You can provide your own templates for the overall changelog structure, as well as for individual sections and commit entries. This allows you to create a changelog that matches your project's style and requirements.

As an example lets say we want to use a variant of the Keep a Changelog format with the emojis and grouping from the previous example.

For that we need to define four templates:

  • Header: The template for the changelog header.
  • Footer: The template for the changelog footer.
  • Commit: The template for individual commit entries.
  • Main: The template for the overall changelog structure.

Header Template

The header template will contain the initial part of the Keep a Changelog format.

handlebars
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

The footer will contain just a note about the changelog being generated by Versu.

handlebars

---
*Generated by versu*

Commit Template

The commit template will define how individual commits are displayed in the changelog. In this case, we want to show the commit subject and scope (if available) following Keep a Changelog's style.

handlebars
- {{subject}} {{#if scope}} ({{scope}}){{/if}}

Main Template

The main template will define the overall structure of the changelog, including how versions and sections are organized.

handlebars
{{~#if prepend}}{{> header}}
{{prependPlaceholder}}

{{/if~}}
{{~#if version}}
{{~#if linkCompare}}
## [{{version}}]({{repoUrl}}/compare/{{previousTag}}...{{currentTag}}) - {{date}}
{{~else}}
## [{{version}}]({{repoUrl}}/releases/tag/{{version}}) - {{date}}
{{~/if}}
{{~else}}
{{~#if previousTag}}
## [Unreleased]({{repoUrl}}/compare/{{previousTag}}...HEAD) - {{date}}
{{~else}}
## Unreleased
{{~/if}}
{{~/if}}

{{#each commitGroups}}

### {{title}}

{{#each commits}}
{{> commit}}
{{/each}}
{{/each}}
{{#if prepend}}{{> footer}}{{/if}}

Note that in the main template we are using the commit partial to render each individual commit entry, and we are also conditionally rendering the header and footer based on whether this is a new changelog or an update to an existing one, which is determined by the presence of the prepend variable.

Variables like previousTag, currentTag, prepend are provided by Versu during the changelog generation process, allowing you to create dynamic and informative changelogs.

In addition prependPlaceholder is a special variable that needs to be set in your context in order for Versu to know where to insert the new changelog content when updating an existing changelog file.

Putting It All Together

Place the templates in a templates folder in your project root, and then reference them in your versu.config.js as shown below:

javascript
import * as fs from "fs";
import * as path from "path";

const cwd = import.meta.dirname;

// Assume the files 
const templatesPath = path.join(cwd, "templates");

const mainTemplate = fs.readFileSync(path.join(templatesPath, "main.hbs"), "utf-8");
const commitTemplate = fs.readFileSync(path.join(templatesPath, "commit.hbs"), "utf-8");
const headerTemplate = fs.readFileSync(path.join(templatesPath, "header.hbs"), "utf-8");
const footerTemplate = fs.readFileSync(path.join(templatesPath, "footer.hbs"), "utf-8");

const context = {
    prependPlaceholder: "<!-- versu-changelog-placeholder -->",
};

const options = {
    // How to group the commits (mapped in transform)
    groupBy: "type",
    // The order sections appear in the changelog
    commitGroupsSort,
    // Transform raw commit data into the new format
    transform,
    // Custom templates
    mainTemplate,
    headerPartial: headerTemplate,
    footerPartial: footerTemplate,
    commitPartial: commitTemplate,
};

export default {
  // Other configuration options
  changelog: {
    root: {
      context,
      options
    },
    module: {
      context,
      options
    },
  },
};

For more details on how to customize the templates and the available variables, refer to the conventional-changelog-writer documentation.

Example Output

Let's see the configuration in action on the running example from Multi-Module Projects: the my-monorepo workspace with the common, auth, api, web and mobile modules.

We will pick right after common receives a new feature (feat: add shared validation helper) and is released as 2.4.0. Since common was already released before (as 2.3.1), the version heading links a comparison between the two release tags. The generated packages/common/CHANGELOG.md would look like this:

markdown
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!-- versu-changelog-placeholder -->

## [2.4.0](https://github.com/my-org/my-monorepo/compare/common@2.3.1...common@2.4.0) - 2026-07-11

### ✨ Features

- add shared validation helper

---
*Generated by versu*

The other modules (auth, api, web and mobile) get the same treatment in their own CHANGELOG.md files as their versions cascade - see Dependency Cascade.

Next Steps


Ready to customize your release notes? Check out the Release Notes guide!

Released under the MIT License.