react-in-app-updates
v1.0.2
Published
A dynamic, light-weight in-app changelog and announcement feed widget for React. Driven by your own backend API or static JSON.
Maintainers
Keywords
Readme
react-in-app-updates
The easiest way to show your users what's new — right inside your app.
Drop a bell icon into your navbar. Your users click it. They see a beautiful, scrollable feed of your latest product updates, bug fixes, and announcements — pulled live from your own backend.
[!NOTE] Designed specifically for React Web Applications (Vite, Next.js, Create React App, etc.).
No third-party SaaS. No monthly fee. Just your data, your design, your component.
Install
npm install react-in-app-updatesMake sure you also have these in your project (you probably already do):
npm install react react-dom lucide-reactQuick Start
Add the component anywhere in your app — a navbar, a header, a sidebar button. It handles everything else.
import { DynamicDailyUpdates } from 'react-in-app-updates';
function Navbar() {
return (
<nav>
<span>My App</span>
{/* That's it. A bell icon appears. Click it to see your updates. */}
<DynamicDailyUpdates apiUrl="/api/changelog" />
</nav>
);
}Your /api/changelog endpoint just needs to return a simple JSON array. See the format below.
Choose a Layout
Four ways to display your updates panel. Pick what fits your UI best.
<DynamicDailyUpdates
apiUrl="/api/changelog"
layout="popover" // "drawer-right" | "drawer-left" | "modal" | "popover"
title="What's New"
/>| Layout | What it looks like |
|---|---|
| drawer-right (default) | Slides in from the right side of the screen |
| drawer-left | Slides in from the left side of the screen |
| modal | Pops up as a centered dialog |
| popover | Floats right below your trigger button (great for navbars) |
Theming
Use a Prebuilt Theme
We ship five premium themes out of the box. Just import and use:
import { DynamicDailyUpdates, PRESET_THEMES } from 'react-in-app-updates';
<DynamicDailyUpdates
apiUrl="/api/changelog"
theme={PRESET_THEMES.midnight}
/>| Theme Key | Style |
|---|---|
| PRESET_THEMES.mono | (Default) Clean, minimal monochrome — works on any site |
| PRESET_THEMES.midnight | Deep dark navy — perfect for dark-mode apps |
| PRESET_THEMES.nord | Soft Nordic pastel — calm and readable |
| PRESET_THEMES.tokyo | Vibrant neon Tokyo Night — bold and modern |
| PRESET_THEMES.cyberpunk | High-contrast neon — for apps that like to stand out |
Build Your Own Theme
If you want to match your own brand colours, pass a theme object with any of these values:
const MY_THEME = {
primary: '#7c3aed', // Your brand accent colour (buttons, badges, indicators)
background: '#0f0f11', // The panel background
cardBackground: '#1a1a1e', // Each update card's background
text: '#f4f4f5', // Headings and titles
textMuted: '#a1a1aa', // Dates and body text
border: '#27272a', // Lines and borders between cards
};
<DynamicDailyUpdates apiUrl="/api/changelog" theme={MY_THEME} />You only need to override the values you want to change — everything else falls back to the default.
All Options
Everything you can pass to the component:
| Option | What it does | Default |
|---|---|---|
| apiUrl | The URL of your backend endpoint that returns the updates list | — |
| apiHeaders | Any extra headers to send with the request (e.g. auth tokens) | {} |
| updates | Skip the API entirely — pass a static array of updates directly | — |
| title | The heading text shown at the top of the panel | "Updates & Changelog" |
| storageKey | The name used to track which update the user last read (stored in browser) | "app_last_viewed_update" |
| layout | How and where the panel appears (drawer-right, drawer-left, modal, popover) | "drawer-right" |
| theme | Your colour config or a PRESET_THEMES.* value | Monochrome default |
| renderTrigger | Replace the default bell button with your own custom button | — |
| renderCategory | Replace the default category badge with your own icon or label | — |
| isOpen | Control the open/closed state from outside the component | — |
| onOpenChange | A function called whenever the panel opens or closes | — |
Custom Trigger Button
Don't want the default bell icon? Render your own button instead:
<DynamicDailyUpdates
apiUrl="/api/changelog"
renderTrigger={(openPanel, hasUnread) => (
<button onClick={openPanel} className="my-custom-btn">
What's New
{hasUnread && <span className="red-dot" />}
</button>
)}
/>openPanel— call this to open the updates panelhasUnread—trueif there are updates the user hasn't seen yet (great for showing a dot or badge)
Custom Category Icons
By default, the component shows a small coloured dot badge per category (feature, fix, announcement, etc.).
You can swap this out for your own icon, SVG, or any React component using renderCategory:
import { Sparkles, Wrench, Megaphone } from 'lucide-react';
<DynamicDailyUpdates
apiUrl="/api/changelog"
renderCategory={(category) => {
if (category === 'feature') return <span className="badge green"><Sparkles size={10} /> New Feature</span>;
if (category === 'fix') return <span className="badge amber"><Wrench size={10} /> Bug Fix</span>;
return <span className="badge gray"><Megaphone size={10} /> {category}</span>;
}}
/>You can also bring your own custom SVG directly:
renderCategory={(category, update) => (
<span dangerouslySetInnerHTML={{ __html: update.iconSvg }} />
)}Or map an iconKey sent from your backend to a component library of your choice.
What Your API Should Return
Your backend just needs to return a JSON array. Here's what each item can include:
[
{
"id": "update-042",
"title": "Dark mode is here 🌙",
"description": "You can now switch to dark mode from your profile settings.",
"date": "2024-06-15",
"category": "feature",
"categoryColor": "#7c3aed",
"imageUrl": "https://your-cdn.com/dark-mode-preview.png",
"link": "https://docs.yourapp.com/changelog/dark-mode"
}
]What each field does
| Field | Required | What it controls |
|---|---|---|
| id | ✅ | A unique ID for this update (used to track what the user has already read) |
| title | ✅ | The headline of the update |
| description | ✅ | A short paragraph describing what changed |
| date | ✅ | The date to display (any readable format, e.g. "2024-06-15") |
| category | ✅ | A label like "feature", "fix", or "announcement" — shown as a badge |
| categoryColor | ☑️ | Override the badge colour with any hex code (e.g. "#f43f5e") |
| imageUrl | ☑️ | A link to an image — renders as a banner card below the description |
| videoUrl | ☑️ | A link to an .mp4 video — renders as an embedded player |
| link | ☑️ | A URL — adds a "Learn more →" button that opens in a new tab |
✅ Required ☑️ Optional
Rich media examples
Add an image banner:
{ "imageUrl": "https://your-cdn.com/screenshot.png" }Embed a video demo:
{ "videoUrl": "https://your-cdn.com/feature-demo.mp4" }Custom badge colour per update:
{ "category": "launch", "categoryColor": "#f59e0b" }Add a "Learn more" link:
{ "link": "https://docs.yourapp.com/releases/v2" }Skip the API — Use Static Data
If you're not ready for a backend or just want to hardcode some updates, pass them directly:
const UPDATES = [
{
id: '1',
title: 'Welcome to the changelog!',
description: 'Updates will appear here as your product grows.',
date: '2024-06-01',
category: 'announcement',
},
];
<DynamicDailyUpdates updates={UPDATES} />Unread Badge — How It Works
The component automatically tracks which update the user last saw using localStorage. When a new update appears at the top of the list, a pulsing dot appears on the bell icon to let the user know. Once they open the panel, it's marked as read.
You can customise the storage key if you have multiple instances:
<DynamicDailyUpdates apiUrl="/api/changelog" storageKey="my_app_changelog_seen" />License
MIT
