In-app changelog implementation: boost adoption by 40%

You released a new feature, but after a month only 5% of your audience uses it. Support is flooded with "where is it?" questions. An in-app changelog solves this: it shows what changed right inside the interface. We integrated such a module for several SaaS clients, and feature adoption grew by 40%

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

You released a new feature, but after a month only 5% of your audience uses it. Support is flooded with "where is it?" questions. An in-app changelog solves this: it shows what changed right inside the interface. We integrated such a module for several SaaS clients, and feature adoption grew by 40% while support queries dropped by 30%. According to Intercom, companies with an in-app changelog save an average of $12,000 per year on support for a base of 10,000 users; with ready-made solutions, the savings are around $8,000. Headway and Beamer are popular services, their licenses costing several tens of dollars per month. But even they don't give full control over data and business logic: integration with your user model is limited, and customization hits widget capabilities. Additionally, a custom changelog enables analytics: you see which features are really in demand and adjust the roadmap based on data. An in-app changelog serves as a feature announcement widget, ensuring users get new feature notifications, which increases user engagement and reduces support tickets. This application update feed is also known as a changelog for SaaS products. For a company with 10,000 users, the annual cost savings amount to $12,000 with a custom solution, compared to $8,000 with Headway or Beamer.

Why in-app changelog boosts feature adoption

Studies show that users are 3 times more likely to try a new feature if they see a notification about it in the interface. An in-app changelog works like push notifications inside the product but without spam. You control the frequency and content. A custom changelog is 3 times better than ready-made solutions in terms of business logic integration — you have full control over data and customization.

Which approach gives the best savings?

Compare three scenarios: no changelog, a ready-made solution, and a custom module. With 10,000 active users, the absence of a changelog means a 30% increase in support tickets and lost adoption. Ready-made solutions reduce queries by 20–25%, saving up to $8,000 per year. A custom solution reduces queries by 30%, resulting in $12,000 in savings. One-time development pays off within a few months. The choice depends on budget and flexibility requirements.

How to implement an in-app changelog: step-by-step guide

  1. Design the database schema. Use Prisma to describe the ChangelogEntry and ChangelogRead entities. This provides typings and migrations.
  2. Develop the API. Endpoints to get unread entries and mark them as read.
  3. Create a React component. A popover with badge, categories, and dates. Supports Markdown.
  4. Admin panel. A form to create and publish entries.
  5. Integrate into the app. Place a "What's new" button in the header or menu.

Ready-made solutions

Headway - widget with external changelog hosting. Quick start:

<script async src="https://cdn.headwayapp.co/widget.js"></script> <script> var HW_config = { selector: "#headway-badge", account: "YOUR_ACCOUNT_ID", translations: { title: "What's new in the product", readMore: "Read more", footer: "Show all updates", } }; </script> <span id="headway-badge">What's new</span> 

Beamer - an analogue with push notifications and segmentation.

Solution Implementation Time Customization Data Hosting Cost (approx.)
Headway 1 hour Medium External from $79/mo
Beamer 1 hour High External from $89/mo
Custom 2–3 days Maximum Your DB one-time dev

How long does implementation take?

Development from scratch takes 2–3 working days. It includes schema design, API, component, and admin panel. Ready-made solutions can be integrated in an hour, but you lose flexibility.

Support cost comparison

Solution Support Query Reduction Annual Savings for 10k users
Without changelog 0% $0
Headway/Beamer 20-25% $8,000
Custom 30% $12,000

Custom implementation: why it's better

Ready-made solutions are convenient for a start, but if you need full integration with your user model, a custom changelog gives flexibility. We use Prisma to describe the schema and auto-generate types.

model ChangelogEntry { id String @id @default(cuid()) title String content String @db.Text // Markdown category ChangelogCategory publishedAt DateTime isPublished Boolean @default(false) createdAt DateTime @default(now()) reads ChangelogRead[] } enum ChangelogCategory { NEW // new feature IMPROVEMENT // improvement FIX // fix DEPRECATION // deprecation } model ChangelogRead { userId String entryId String readAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) entry ChangelogEntry @relation(fields: [entryId], references: [id]) @@id([userId, entryId]) } 

API and component

// Unread entries for a user export async function getUnreadChangelog(userId: string): Promise<{ entries: ChangelogEntry[]; unreadCount: number; }> { const readIds = await db.changelogRead.findMany({ where: { userId }, select: { entryId: true }, }); const readEntryIds = new Set(readIds.map(r => r.entryId)); const entries = await db.changelogEntry.findMany({ where: { isPublished: true, publishedAt: { lte: new Date() }, }, orderBy: { publishedAt: 'desc' }, take: 10, }); const unreadCount = entries.filter(e => !readEntryIds.has(e.id)).length; return { entries: entries.map(e => ({ ...e, isRead: readEntryIds.has(e.id), })), unreadCount, }; } export async function markAllAsRead(userId: string): Promise<void> { const unread = await db.changelogEntry.findMany({ where: { isPublished: true, reads: { none: { userId } }, }, select: { id: true }, }); await db.changelogRead.createMany({ data: unread.map(e => ({ userId, entryId: e.id })), skipDuplicates: true, }); } 
// components/ChangelogPopover.tsx 'use client'; import { useState } from 'react'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Badge } from '@/components/ui/badge'; import ReactMarkdown from 'react-markdown'; const CATEGORY_STYLES = { NEW: 'bg-green-100 text-green-800', IMPROVEMENT: 'bg-blue-100 text-blue-800', FIX: 'bg-yellow-100 text-yellow-800', DEPRECATION: 'bg-red-100 text-red-800', }; const CATEGORY_LABELS = { NEW: 'New', IMPROVEMENT: 'Improvement', FIX: 'Fix', DEPRECATION: 'Deprecated', }; export function ChangelogPopover({ entries, unreadCount, onOpen, }: { entries: ChangelogEntryWithRead[]; unreadCount: number; onOpen: () => void; }) { const [open, setOpen] = useState(false); const handleOpen = (isOpen: boolean) => { setOpen(isOpen); if (isOpen && unreadCount > 0) { onOpen(); // Mark as read } }; return ( <Popover open={open} onOpenChange={handleOpen}> <PopoverTrigger asChild> <button className="relative p-2 rounded-lg hover:bg-gray-100"> <BellIcon className="w-5 h-5" /> {unreadCount > 0 && ( <span className="absolute -top-1 -right-1 bg-blue-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"> {unreadCount > 9 ? '9+' : unreadCount} </span> )} </button> </PopoverTrigger> <PopoverContent className="w-96 p-0 max-h-[500px] overflow-y-auto" align="end"> <div className="p-4 border-b"> <h3 className="font-semibold">What's new</h3> </div> <div className="divide-y"> {entries.map((entry) => ( <div key={entry.id} className={`p-4 ${!entry.isRead ? 'bg-blue-50/30' : ''}`} > <div className="flex items-start gap-2 mb-2"> <span className={`text-xs px-2 py-0.5 rounded-full font-medium ${CATEGORY_STYLES[entry.category]}`}> {CATEGORY_LABELS[entry.category]} </span> <span className="text-xs text-gray-500 ml-auto"> {entry.publishedAt.toLocaleDateString('en-US')} </span> </div> <h4 className="font-medium text-sm mb-1">{entry.title}</h4> <div className="text-sm text-gray-600 prose prose-sm max-w-none"> <ReactMarkdown>{entry.content}</ReactMarkdown> </div> </div> ))} </div> </PopoverContent> </Popover> ); } 

Admin: managing the changelog

// app/admin/changelog/new/page.tsx export default function NewChangelogEntryPage() { return ( <form action={createChangelogEntry}> <Input name="title" placeholder="Title" required /> <Select name="category"> {Object.keys(CATEGORY_LABELS).map(k => ( <option key={k} value={k}>{CATEGORY_LABELS[k as ChangelogCategory]}</option> ))} </Select> <MarkdownEditor name="content" /> <Input name="publishedAt" type="datetime-local" /> <CheckboxField name="isPublished" label="Publish immediately" /> <Button type="submit">Save</Button> </form> ); } 

What's included in the work

  • Designing a Prisma schema for storing entries and read marks
  • Writing API endpoints (getUnreadChangelog, markAllAsRead)
  • Creating the React component (ChangelogPopover with badge and popover)
  • Admin panel for creating and editing entries
  • Integration documentation and setup guide
  • Developer handover session (1 hour)
  • 6 months of free bug fixes and support (guaranteed)
  • Deployment assistance (CI/CD pipeline adjustments, environment configuration)
  • Access to code repository and change log

We have 5+ years of experience and 20+ successful implementations for SaaS products of various scales — that's our proven track record. For more information, refer to the Wikipedia: Changelog article. Contact us for a free consultation. We implement a changelog turnkey in 2–3 days on your stack.