Changelog

Maintain a public changelog of new features, improvements, and fixes

The boilerplate ships with a single-page changelog at /changelog that lets you publish release notes without setting up a CMS, database table, or extra build step. Entries live in a typed TypeScript file, so adding a release is a one-file commit.

Where things live

ConcernFile
Release data (the only file you edit per release)app/data/changelog.ts
Rendering (layout, year grouping, styling)app/pages/changelog/index.vue
Header nav entryapp/components/header/MainHeader.vue
Page chrome copy (title, description, type labels)i18n/locales/*.json (changelog.* keys)

Data shape

app/data/changelog.ts exports two things: the types and the entries.

app/data/changelog.ts
export type ChangeType = 'feature' | 'improvement' | 'fix'

export interface Change {
  type: ChangeType
  text: string
}

export interface ChangelogEntry {
  version: string  // e.g. "1.2.0" — used as the anchor id
  date: string     // ISO yyyy-mm-dd
  changes: Change[]
}

export const CHANGELOG_ENTRIES: ChangelogEntry[] = [
  // newest first
]

Rules:

  • Entries are ordered newest first. The page does not re-sort them.
  • version is a plain string. Semver (1.2.0) is recommended but not enforced.
  • date must be ISO yyyy-mm-dd. The page renders a relative date ("2 weeks ago") with the absolute date in a hover tooltip.
  • Each version becomes a deep-linkable anchor like /changelog#v1-2-0.

Change type taxonomy

Pick one of three types per change. The page renders each with an emoji and a translated type label:

  • feature — new capability that didn't exist before. New page, new integration, new API. Rendered with ✨.
  • improvement — meaningful change to existing behavior. Refactors users will notice, performance wins, UX polish, expanded docs. Rendered with ⚡.
  • fix — bug fixes and regressions. Rendered with 🐛.

If you find yourself wanting more types (e.g. security, breaking, deprecated), extend ChangeType in app/data/changelog.ts and map the new value to an emoji in app/pages/changelog/index.vue (changeEmoji).

Adding a release

  1. Open app/data/changelog.ts.
  2. Copy the top entry and paste it above itself.
  3. Bump version and set date to today.
  4. Replace changes with the new entries.

That's the whole workflow. No migrations, no CMS, no rebuild story — TypeScript validates the shape at compile time.

Why entry text isn't translated

The boilerplate's i18n covers the page chrome (title, description, type labels) but not the change text itself. Release notes are usually authored once per release in a single language by whoever shipped the change. Forcing them through $t() would mean opening four locale files per release, and untranslated entries would silently fall back to the key string.

If your project genuinely needs translated release notes, opt in at the call site:

app/data/changelog.ts
{ type: 'feature', text: 'changelog.entries.v1_2_0.faster_search' }

Then resolve it in the template:

app/pages/changelog/index.vue
<span class="text-sm leading-6">{{ $t(change.text) }}</span>

You'll need to add the key to every locale file. For most projects this is more friction than benefit; default to plain strings.

Reusing the data elsewhere

CHANGELOG_ENTRIES is a plain export, so anything in the app can import it. Some ideas:

  • A "What's new" card on the landing page that surfaces the latest entry.
  • A /api/changelog endpoint that returns the array as JSON for external consumers.
  • An RSS feed at /changelog.xml built in a Nitro route from the same array.

The data file has no Nuxt or Vue imports, so it can be consumed from server routes, plugins, or other components without complication.

Removing the changelog

If your project doesn't need a public changelog, remove these five things:

  1. Delete app/data/changelog.ts.
  2. Delete app/pages/changelog/index.vue.
  3. Delete this docs file (content/docs/4.customization/5.changelog.md).
  4. Remove the changelog block and header.navigation.changelog key from each i18n/locales/*.json file.
  5. Remove the /changelog entry from navigationItems in app/components/header/MainHeader.vue and app/components/header/LandingHeader.vue (and the unused History icon imports from both files).

No database migrations or build config changes are needed.

The seed entries shipped with the boilerplate are example data based loosely on this project's own history. Replace them before launching your project.