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.
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.
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.
Footer Template
The footer will contain just a note about the changelog being 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.
Main Template
The main template will define the overall structure of the changelog, including how versions and sections are organized.
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:
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:
# 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
- Release Notes - The release notes counterpart
- Pre-release Versions - Pre-release setup
- Configuration File - Full config reference
- Examples - Real examples
Ready to customize your release notes? Check out the Release Notes guide!
