The open-source product adoption toolkit.
Changelogs • Tours • Checklists • Hotspots • Feedback — from your own codebase.
< 3 kB core · Zero vendor lock-in · MIT licensed
Quickstart • Components • Playground • API Reference • Migration Guide
Every SaaS ships features. Users miss them. The usual options are bad:
| Option | Problem |
|---|---|
| Beamer / Headway / AnnounceKit | External widget, vendor lock-in, $49–399/mo |
| Pendo / Appcues | Feature flags AND onboarding, ~$7k+/yr |
| Joyride / Shepherd.js | Tours only, not persistence or changelog |
| DIY | You build it, forget expiry, badges stay forever |
FeatureDrop gives you a free, self-hosted middle path: production-ready adoption components that run inside your own React tree, powered by a JSON manifest you own.
npm install featuredrop # < 3 kB core, zero runtime dependencies
1. Define your features:
import { createManifest } from 'featuredrop'
export const features = createManifest([
{
id: 'dark-mode',
label: 'Dark Mode',
description: 'Full dark theme support across every surface.',
releasedAt: '2026-03-01T00:00:00Z',
showNewUntil: '2026-04-01T00:00:00Z',
type: 'feature',
priority: 'high',
cta: { label: 'Try it', url: '/settings/appearance' },
},
])
2. Wrap your app:
import { FeatureDropProvider } from 'featuredrop/react'
import { LocalStorageAdapter } from 'featuredrop'
import { features } from './features'
<FeatureDropProvider manifest={features} storage={new LocalStorageAdapter()}>
<App />
</FeatureDropProvider>
3. Add badges and a changelog:
import { NewBadge, ChangelogWidget } from 'featuredrop/react'
// Sidebar nav item
<a href="/settings">
Settings <NewBadge id="dark-mode" /> {/* auto-expires */}
</a>
// Changelog button
<ChangelogWidget title="What's new" showReactions />
That's it. Badges expire on schedule. No database setup. No vendor account. No tracking pixels.
→ Full walkthrough: 10-minute quickstart
Everything you'd get from Beamer or Pendo, but free, self-hosted, and headless-first.
| Component | Description |
|---|---|
<ChangelogWidget> |
Trigger button + slide-out/modal changelog with emoji reactions |
<ChangelogPage> |
Full-page changelog with filters, search, and pagination |
<NewBadge> |
Auto-expiring pill / dot / count badge |
<Banner> |
Top-of-page or inline banner with announcement, warning, info variants |
<Toast> |
Stackable toast notifications with auto-dismiss and position control |
<Tour> |
Multi-step guided product tours with keyboard nav and persistence |
<Checklist> |
Onboarding task checklists with progress tracking |
<Spotlight> |
Pulsing DOM-attached beacon/tooltip |
<SpotlightChain> |
Chained spotlight walkthrough ("here are 3 new things") |
<AnnouncementModal> |
Priority-gated modal with optional image carousel |
<Hotspot> / <TooltipGroup> |
Persistent contextual hints with visibility caps |
<FeedbackWidget> |
In-app feedback with category, emoji, screenshot support |
<Survey> |
NPS / CSAT / CES / custom survey engine with trigger rules |
<FeatureRequestButton> |
Per-feature voting button with vote guard |
<FeatureRequestForm> |
Request capture + sortable request list |
All components are headless-capable via render props. See live demos →
Manifest (static) Storage (runtime)
┌─────────────────────┐ ┌──────────────────────┐
│ releasedAt: Mar 1 │ │ watermark ← server │
│ showNewUntil: Apr 1 │ │ dismissed ← localStorage │
└──────────┬──────────┘ └──────────┬───────────┘
│ │
└──────────┐ ┌─────────────────┘
▼ ▼
┌───────────────┐
│ isNew() │
│ │
│ !dismissed │
│ !expired │
│ afterWatermark│
└───────┬───────┘
│
true / false
New users see everything (no watermark). Returning users see only features shipped since their last visit. Dismissals are instant (localStorage). "Mark all seen" syncs across devices with one optional server write.
Read the full Architecture doc for cross-device sync and custom adapter patterns.
| Adapter | Import | Best For |
|---|---|---|
LocalStorageAdapter |
featuredrop |
Browser apps (default) |
MemoryAdapter |
featuredrop |
Testing, SSR |
IndexedDBAdapter |
featuredrop/adapters |
Offline-first PWAs |
RemoteAdapter |
featuredrop/adapters |
Server-backed with retry + circuit-breaker |
HybridAdapter |
featuredrop/adapters |
Local + remote with batched flush |
| Redis / PostgreSQL / DynamoDB | featuredrop/adapters |
Database-backed server-side apps |
Manage your manifest from the command line:
# Scaffold
npx featuredrop init
npx featuredrop add --label "Dark Mode" --category ui --type feature
# Validate & audit
npx featuredrop validate # schema + duplicate ID check
npx featuredrop doctor # security + best practice audit
npx featuredrop stats # manifest summary stats
# Build (markdown → JSON)
npx featuredrop build --pattern "features/**/*.md" --out featuredrop.manifest.json
# Generate outputs
npx featuredrop generate-rss --out featuredrop.rss.xml
npx featuredrop generate-changelog --out CHANGELOG.generated.md
# Migrate from vendors
npx featuredrop migrate --from beamer --input beamer-export.json
npx featuredrop migrate --from headway --input headway-export.json
npx featuredrop migrate --from announcekit --input announcekit-export.json
npx featuredrop migrate --from canny --input canny-export.json
npx featuredrop migrate --from launchnotes --input launchnotes-export.json
| Framework | Status | Import |
|---|---|---|
| React / Next.js | ✅ Stable | featuredrop/react |
| Vanilla JS | ✅ Stable | featuredrop |
| SolidJS | 🔬 Preview | featuredrop/solid |
| Preact | 🔬 Preview | featuredrop/preact |
| Web Components | 🔬 Preview | featuredrop/web-components |
| Angular | 🔬 Preview | featuredrop/angular |
| Vue 3 | 🔬 Preview | featuredrop/vue |
| Svelte 5 | 🔬 Preview | featuredrop/svelte |
Don't want our components? Use hooks — data + actions, zero JSX:
import { useChangelog } from 'featuredrop/react/hooks'
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
import { Badge } from '@/components/ui/badge'
function MyChangelog() {
const { newFeatures, newCount, dismiss, markAllSeen } = useChangelog()
return (
<Sheet onOpenChange={() => markAllSeen()}>
<SheetTrigger>
What's New {newCount > 0 && <Badge>{newCount}</Badge>}
</SheetTrigger>
<SheetContent>
{newFeatures.map(f => (
<div key={f.id} onClick={() => dismiss(f.id)}>
<h3>{f.label}</h3>
<p>{f.description}</p>
</div>
))}
</SheetContent>
</Sheet>
)
}
| Hook | Import | Returns |
|---|---|---|
useFeatureDrop() |
featuredrop/react/hooks |
Full context: features, count, dismiss, throttle controls |
useNewFeature(key) |
featuredrop/react/hooks |
{ isNew, feature, dismiss } |
useNewCount() |
featuredrop/react/hooks |
Current unread badge count |
useChangelog() |
featuredrop/react/hooks |
{ features, newFeatures, newCount, dismiss, dismissAll, markAllSeen, getByCategory } |
useTour(id) |
featuredrop/react/hooks |
Imperative tour controls and step snapshot |
useTourSequencer(sequence) |
featuredrop/react/hooks |
Ordered multi-tour orchestration |
useChecklist(id) |
featuredrop/react/hooks |
Checklist progress + task controls |
useSurvey(id) |
featuredrop/react/hooks |
Survey controls: show, hide, askLater |
useTabNotification() |
featuredrop/react/hooks |
Browser tab title count: "(3) My App" |
When to use hooks vs components: If your project uses shadcn/ui, Radix, or any custom design system, use hooks from
featuredrop/react/hooks. If you want out-of-the-box UI, use components fromfeaturedrop/react.
Pre-built components that use shadcn primitives for UI + FeatureDrop hooks for logic. Install via the shadcn CLI:
npx shadcn@latest add https://featuredrop.dev/r/changelog-widget.json
| Component | Install |
|---|---|
| New Badge | npx shadcn@latest add https://featuredrop.dev/r/new-badge.json |
| Changelog Widget | npx shadcn@latest add https://featuredrop.dev/r/changelog-widget.json |
| Tour | npx shadcn@latest add https://featuredrop.dev/r/tour.json |
| Checklist | npx shadcn@latest add https://featuredrop.dev/r/checklist.json |
| Feedback Widget | npx shadcn@latest add https://featuredrop.dev/r/feedback-widget.json |
Components land in components/featuredrop/ — you own the code. Full docs →
Try it now:
FeatureDrop is built for the AI coding era. Your AI assistant already knows how to use it.
# Install the plugin — Claude Code learns FeatureDrop's API automatically
/plugin install featuredrop
Then just ask: "Add a changelog widget with auto-expiring badges to my app" — Claude handles the rest.
# Search and install the FeatureDrop skill
npx ctx7 skills search FeatureDrop
Any AI coding assistant with Context7 support gets up-to-date FeatureDrop docs, API reference, and best practices injected into context automatically.
# Auto-detect your IDE and copy the right context files
npx featuredrop ai-setup
// tailwind.config.ts
import { featureDropPlugin } from 'featuredrop/tailwind'
export default {
plugins: [featureDropPlugin()],
// Adds: fd-badge, fd-badge-dot, fd-badge-count, animations, CSS variables
// Auto dark mode, reduced-motion support
}
Fan out release notifications to Slack, Discord, email, webhooks, or RSS on deploy:
import { SlackBridge, DiscordBridge, WebhookBridge, EmailDigestGenerator, RSSFeedGenerator } from 'featuredrop/bridges'
await SlackBridge.notify(feature, { webhookUrl: process.env.SLACK_WEBHOOK! })
await DiscordBridge.notify(feature, { webhookUrl: process.env.DISCORD_WEBHOOK! })
await WebhookBridge.post(feature, { url: 'https://api.example.com/hooks/features' })
const html = EmailDigestGenerator.generate(features, { title: 'Weekly Product Updates' })
const rss = RSSFeedGenerator.generate(features, { title: 'Product Updates' })
Pipe adoption events to any analytics provider:
<FeatureDropProvider
manifest={features}
storage={storage}
analytics={{
onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
onFeatureClicked: (f) => posthog.capture('feature_clicked', { id: f.id }),
onWidgetOpened: () => posthog.capture('changelog_opened'),
}}
>
<App />
</FeatureDropProvider>
Works with PostHog, Mixpanel, Amplitude, Segment, or any custom endpoint.
Show the right features to the right users:
<FeatureDropProvider
manifest={features}
storage={storage}
userContext={{ plan: 'pro', role: 'admin', region: 'eu' }}
>
<App />
</FeatureDropProvider>
Define audience rules per feature in your manifest:
{
"id": "ai-copilot",
"label": "AI Copilot",
"audience": { "plan": ["pro", "enterprise"], "region": ["us", "eu"] }
}
Users outside the audience never see the feature. No server calls. No feature flag service needed.
Validate your manifest in every pull request:
import {
diffManifest,
generateChangelogDiff,
generateChangelogDiffMarkdown,
validateManifestForCI
} from 'featuredrop/ci'
const diff = diffManifest(beforeManifest, afterManifest)
const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
const markdown = generateChangelogDiffMarkdown(diff, { includeFieldChanges: true })
const validation = validateManifestForCI(afterManifest)
pnpm size-check # bundle budget check post-build
npx featuredrop migrate --from beamer --input beamer-export.json --out features.json
| Beamer | Pendo | FeatureDrop | |
|---|---|---|---|
| Price | $59–399/mo | $7k+/yr | Free (MIT) |
| Bundle impact | External script | ~300 kB agent | < 3 kB core |
| Vendor lock-in | Yes | Yes | No |
| Data ownership | Vendor-hosted | Vendor-hosted | Your repo |
| Customization | CSS themes | Limited | Full source access |
| FeatureDrop | Beamer | Headway | AnnounceKit | Pendo | |
|---|---|---|---|---|---|
| Price | Free | $59–399/mo | $49–249/mo | $79–299/mo | $7k+/yr |
| Auto-expiring badges | ✅ | — | — | — | — |
| Changelog widget | ✅ | ✅ | ✅ | ✅ | ✅ |
| Product tours | ✅ | — | — | — | ✅ |
| Onboarding checklists | ✅ | — | — | — | ✅ |
| Spotlight / beacon | ✅ | — | — | — | — |
| Hotspot tooltips | ✅ | — | — | — | — |
| Announcement modal | ✅ | — | — | — | — |
| Toast notifications | ✅ | — | — | — | — |
| Feedback & surveys | ✅ | — | — | — | ✅ |
| Feature request voting | ✅ | — | — | — | — |
| Tab title notification | ✅ | — | — | — | — |
| Zero runtime deps (core) | ✅ | — | — | — | — |
| Framework agnostic | ✅ | — | — | — | — |
| Headless mode | ✅ | — | — | — | — |
| Analytics callbacks | ✅ | ✅ | ✅ | ✅ | ✅ |
| Self-hosted | ✅ | — | — | — | — |
| Open source | ✅ | — | — | — | — |
| Resource | Description |
|---|---|
| Live Docs | Full documentation site |
| Quickstart | Ship your first badge in 10 minutes |
| Component Gallery | Live interactive demos |
| Playground | Local sandbox + hosted templates |
| API Reference | All functions, hooks, and components |
| Migration Guide | Migrate from Beamer, Pendo, Headway |
| Architecture | Three-check algorithm, cross-device sync |
| Recipes | Copy-paste integration patterns |
| Frameworks | Vue, Svelte, Solid, Angular, Preact, Web Components |
All marketing assets are in apps/docs/public/og/.
| File | Ratio | Use |
|---|---|---|
og.png |
1200×630 (1.91:1) | Website OG / link previews, Discord, Slack |
github-social.png |
1280×640 (2:1) | GitHub repo social preview ← upload this |
twitter-header.png |
1500×500 (3:1) | X.com profile header |
linkedin-banner.png |
1584×396 (4:1) | LinkedIn company page banner |
reddit-16x9.png |
1920×1080 (16:9) | Reddit posts, r/reactjs, r/webdev |
producthunt.png |
1270×760 | Product Hunt launch |
story-9x16.png |
1080×1920 (9:16) | Instagram / LinkedIn Stories |
GitHub social preview: Repo Settings → Social preview → Upload apps/docs/public/og/github-social.png.
See CONTRIBUTING.md for dev setup, commit conventions, and release process.
mainpnpm security-check scans runtime source for unsafe execution patternsMIT © GLINR STUDIOS
Built and battle-tested at AskVerdict AI.
A GLINR STUDIOS open source project.