npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

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-updates

Make sure you also have these in your project (you probably already do):

npm install react react-dom lucide-react

Quick 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 panel
  • hasUnreadtrue if 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