In-Product Release Notes & What's New: Chat Prompts
You ship every week. Users notice maybe 10% of what you ship. The other 90% is invisible because users don't read your blog, follow your Twitter, or check your /changelog page. In-product release notes are the bridge — surface what's new where users already are, when they sign in or hit a relevant feature, and track read-state per user so you only show each update once.
Done well, this lifts feature adoption (users actually try the new thing), reduces "wait, I didn't know that existed" support tickets, and gives marketing a per-feature engagement signal. Done poorly, it becomes notification spam — users dismiss every modal as "more interruption" and feature awareness drops back to zero.
The hard parts: deciding scope (every release vs. only meaningful ones), surface (modal on login vs. notification badge vs. inline tooltips on the affected feature), tracking read-state per user, segmenting by plan or role (don't show "new admin features" to non-admins), and resisting the temptation to use it for everything (release notes ≠ marketing announcements ≠ outage alerts).
When Release Notes Belong (and Don't)
Use in-product release notes for:
- New features users would benefit from trying
- Significant UX changes that change muscle memory ("the navigation moved")
- Power-user features that aren't obvious without prompting
- Plan-relevant updates (new feature on Pro tier; show only to Pro customers)
Don't use in-product release notes for:
- Bug fixes (users don't care; show only on the engineering changelog)
- Marketing announcements (use email, banners, or product blog)
- Urgent service alerts (use a banner — see the status banner prompt below)
- Pricing changes (use email + dedicated in-product flow)
Modal-on-Login Pattern
The classic pattern. User signs in; if there are unread updates, show a modal once.
I'm building "What's New" modal for [product]. When the user signs in:
1. Check the latest release notes against the user's `lastSeenReleaseAt` timestamp
2. If there are unseen entries, show a modal listing them (most recent first, max 3-5)
3. After dismissing, store `lastSeenReleaseAt = now` server-side
4. Don't show again until a new release is published past that timestamp
Data model:
- `releases` table: `{id, publishedAt, title, body, image?, ctaUrl?, ctaLabel?, segments: ['all'|'pro'|'admin'|...]}`
- `users.lastSeenReleaseAt` (timestamp; nullable)
Behavior:
- On sign in: query releases where `publishedAt > user.lastSeenReleaseAt AND user matches segment`
- If 0 results: don't show modal
- If 1+ results: show modal with up to 5 most recent; "View all" links to /whats-new
- "Dismiss" or close button: PATCH user.lastSeenReleaseAt = max(release.publishedAt)
- Don't show on every page load — only on first load per session, and only if the user came from sign in (not navigation)
Implement:
1. The data model (Drizzle schema or Prisma)
2. The query that returns unseen-for-this-user releases (filter by segment)
3. The modal component with carousel or list of releases
4. The hook to trigger the modal on first load post-login
5. The "View all" page at /whats-new
Stack: Next.js App Router + Drizzle/Postgres + TanStack Query + shadcn/ui Dialog.
Notification Badge / Inbox Pattern
Less interrupting than a modal. A badge on a "What's New" icon (often near the user avatar). Click to see the inbox.
I want a less intrusive "What's New" — not a modal, but a notification badge.
Pattern:
1. A bell or sparkles icon in the top nav
2. Badge with count of unread releases
3. Click opens a dropdown / popover listing recent releases
4. Each item has its own read-state (clicked → read)
5. When a release is fully read, badge count decrements
6. "Mark all as read" link clears the badge
Data model:
- `releases` (as before)
- `release_reads` join table: `{userId, releaseId, readAt}` — one row per user per release they've read
Behavior:
- Badge count = releases matching user segment AND user not in `release_reads` AND publishedAt within last [60] days
- Click opens dropdown; clicking an item navigates to /whats-new/[slug] AND inserts `release_reads` row
- Older releases (>60 days) drop off the inbox automatically but remain at /whats-new
Implement:
- The dropdown component with read/unread states
- The hook for badge count (refetch every 5 minutes or on focus)
- The mark-as-read mutation
- The /whats-new index page (paginated history)
- The /whats-new/[slug] detail page
Stack: Next.js App Router + Drizzle/Postgres + TanStack Query + shadcn/ui Popover.
Inline Tooltip on Affected Feature
Instead of (or in addition to) a centralized inbox, surface the news where it's relevant. The user opens the Reports tab; a small "New: Schedule reports" badge appears next to the new feature.
I want to surface release notes inline — when the user encounters a new feature, a "New" badge appears next to it.
Pattern:
1. Each release entry has an optional `affectedSelector` — e.g. `[data-feature="schedule-reports"]`
2. When the user is on a page containing that selector, render a "New" pill next to it
3. Click the pill: a small popover explains the feature + link to docs / changelog
4. Track per-user dismissal — clicking "Got it" stops showing for that user
Data model:
- `releases.affectedFeature: string` (e.g. "schedule-reports")
- A `<NewBadge feature="schedule-reports" />` component that:
- Reads from a context with the list of features unread by this user
- Renders nothing if not in the list
- Renders a pulse-pill if in the list
- Server endpoint: `POST /api/releases/dismiss { feature }` — adds to release_reads for that release
Implementation:
1. The provider that fetches the user's unread features once on app mount
2. The `<NewBadge>` component
3. The popover explaining the feature
4. The dismiss mutation
Stack: Next.js App Router + TanStack Query + shadcn/ui Popover + Tailwind for the pulse animation.
This pattern boosts adoption better than centralized modals because users encounter the news exactly when relevant.
Hybrid: Modal + Badge + Inline
Best results often come from layering all three:
- Modal on login: catches users who just signed in
- Badge in nav: persistent reminder if they dismiss the modal
- Inline pills: surfaces individual features as users explore
I want all three: login modal + nav badge + inline feature pills.
Coordination logic:
- Login modal shows up to 3 most-recent unread releases on sign-in
- Dismissing the modal does NOT mark all as read — just marks "modal seen this session"
- The nav badge still shows (decrements as user reads each release)
- Inline feature pills still show until user clicks them
This way, the modal is the "first impression" and the badge + inline pills are the persistent reminders.
Build the unified state model that tracks:
- `modal_seen_at` per session (per user) — prevents modal re-showing
- `release_reads` per release per user — drives badge + pills
- The modal counts releases as "unread" (showing them) without marking them read on dismiss
Stack: Next.js + Drizzle + TanStack Query.
Segmenting Releases
Not all users should see all releases. Plan-tier features, admin-only features, region-specific updates.
Build segmenting for release notes:
1. Each release has a `segments` array: e.g. `['plan:pro', 'plan:enterprise']` or `['role:admin']` or `['region:eu']`
2. A user matches a segment if they meet the criteria (plan / role / region)
3. The unread query filters releases where the user matches at least one segment
4. Default segment: `['all']`
Show me:
- The segment-matching logic (server-side query in Drizzle)
- The admin UI for tagging a release with segments
- A preview function ("preview as user X") so the publisher can verify segment targeting before going live
Edge cases:
- A user upgrades from Free to Pro mid-month — they should now see Pro-tier release notes from the past 60 days, even if previously hidden
- A user changes role from member to admin — admin-only releases from past 60 days appear in their inbox
Implement.
Stack: Next.js + Drizzle.
Authoring + Publishing Workflow
Release notes need an authoring workflow that's lightweight enough that PMs and engineers actually use it.
Build a minimal authoring flow for release notes:
1. /admin/releases — list all releases (published + drafts)
2. /admin/releases/new — create a new release
- Title (string, max 80 chars)
- Body (Markdown editor — use [TipTap / Lexical] with image upload)
- Optional image (banner image, uploaded to [Vercel Blob / S3])
- Optional video / GIF embed
- Optional CTA button (label + URL)
- Segments (multi-select)
- Affected feature (single string for inline pill)
- Status: draft / scheduled / published
- Publish date (defaults to "now" on publish; can be scheduled future)
3. Preview button — renders the release as it will appear in modal + inbox + inline
4. Publish button — sets status to published; sets publishedAt; immediately visible to matching segments
Authorization: only users with `admin` role on a designated team can publish.
Implement:
- The admin UI
- The Drizzle schema
- The publish endpoint with authorization check
- The preview function
Stack: Next.js App Router + Drizzle + TipTap + Vercel Blob + shadcn/ui forms.
Tracking & Analytics
Release notes only justify themselves if you measure them. What % of users saw the modal? What % clicked through to the new feature?
Add analytics to release notes:
1. Track per release: views (modal shown), dismissals (modal closed without action), CTA clicks, inline-pill clicks, /whats-new visits
2. Per release dashboard: viewers / dismissers / clickers / overall reach (% of segment exposed)
3. Per-feature tracking: did the new feature's adoption (% of segment using it) lift after the release note?
Use [PostHog / Mixpanel / Amplitude] for event tracking. Define events:
- `release_modal_shown` (releaseId, userId)
- `release_modal_dismissed` (releaseId, userId)
- `release_cta_clicked` (releaseId, userId, ctaUrl)
- `release_inline_pill_clicked` (releaseId, feature)
- `release_inbox_opened` (userId)
- `release_marked_read` (releaseId, userId)
Build a simple admin dashboard showing reach + click-through per release.
Stack: Next.js + PostHog + your DB.
Common Pitfalls
Showing the modal on every page load. Drives users insane. Show once per session post-sign-in; trust the badge for re-engagement.
No read-state per user. "Mark all as read" is a session-scoped checkbox; users see the same modal next session. Always track read-state server-side per user.
Releases that never expire. Six-month-old releases shouldn't be in the active inbox. Drop from active inbox after 60-90 days; keep in /whats-new history.
No segmenting. Showing "New admin features" to a free-tier individual contributor means every user sees noise. Segment by plan, role, region.
Releases too granular. Posting a release for every bug fix or copy tweak floods the inbox. Hold release notes for changes a user would actually benefit from knowing.
No CTA. A release that says "we improved performance" with no next action wastes attention. Either link to docs, link to the new feature, or add demo video.
Showing draft releases. Forgetting the published/draft distinction or not gating on it causes embarrassing premature releases. Test the publish flow.
Markdown injection. If non-engineers can author releases and the renderer doesn't sanitize, you have an XSS vector. Use a strict markdown renderer (no raw HTML) or sanitize.
Releases tied to deploy dates. Release notes should track user-visible changes, not deploy events. A backend optimization isn't a release note.
No "View all" / archive. Users want to go back and find the release that mentioned a feature. Maintain /whats-new with full history, paginated, searchable.
Forgetting mobile. Modal layouts that work on desktop break on phone. Test the modal at mobile width; consider a fullscreen sheet on mobile instead.
No way to opt out. Some users genuinely don't want updates. Provide a "Don't show me product updates" preference; respect it (still allow critical updates like security or breaking changes).
Localization neglected. International users see English-only release notes. If you support multiple languages, release notes need translations or fallback messaging.
Releases without screenshots / GIFs. A new feature with no visual is harder to "get" in 5 seconds than one with a 3-second GIF. Invest in visuals.
Treating release notes as marketing. "Our app is the best!" energy turns release notes into adverts. Keep them functional: what changed; how to use it; link to learn more.
Mixing release notes with status alerts. Outage notification ≠ feature release. Different surfaces, different tone, different urgency. Don't put service alerts in /whats-new.
See Also
- Changelog & Roadmap — public-facing changelog
- Onboarding Tour Implementation — for new-user feature introduction
- In-Product Help Center / Knowledge Base
- Toast Notifications UI
- In-App Notifications
- Notification Preferences & Unsubscribe
- Feature Flags — gating new features by segment
- Roles & Permissions — for role-based segmenting
- AB Testing — testing release-note variants
- Product Launch (LaunchWeek) — external launch content
- Customer Health Scoring — feature adoption signals from release notes
- Activation Funnel — release notes as activation lever
- Empty States, Loading & Error States