featuredrop 1.4.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -760
- package/dist/adapters.cjs +1757 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +744 -0
- package/dist/adapters.d.ts +744 -0
- package/dist/adapters.js +1745 -0
- package/dist/adapters.js.map +1 -0
- package/dist/admin.cjs +148 -32
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +14 -3
- package/dist/admin.d.ts +14 -3
- package/dist/admin.js +148 -32
- package/dist/admin.js.map +1 -1
- package/dist/bridges.cjs +111 -13
- package/dist/bridges.cjs.map +1 -1
- package/dist/bridges.d.cts +12 -5
- package/dist/bridges.d.ts +12 -5
- package/dist/bridges.js +111 -13
- package/dist/bridges.js.map +1 -1
- package/dist/ci.cjs +34 -0
- package/dist/ci.cjs.map +1 -1
- package/dist/ci.d.cts +5 -1
- package/dist/ci.d.ts +5 -1
- package/dist/ci.js +34 -1
- package/dist/ci.js.map +1 -1
- package/dist/cms.cjs +835 -0
- package/dist/cms.cjs.map +1 -0
- package/dist/cms.d.cts +236 -0
- package/dist/cms.d.ts +236 -0
- package/dist/cms.js +829 -0
- package/dist/cms.js.map +1 -0
- package/dist/flags.cjs +27 -7
- package/dist/flags.cjs.map +1 -1
- package/dist/flags.d.cts +14 -0
- package/dist/flags.d.ts +14 -0
- package/dist/flags.js +27 -7
- package/dist/flags.js.map +1 -1
- package/dist/index.cjs +52 -4481
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1340
- package/dist/index.d.ts +1 -1340
- package/dist/index.js +53 -4388
- package/dist/index.js.map +1 -1
- package/dist/markdown.cjs +257 -0
- package/dist/markdown.cjs.map +1 -0
- package/dist/markdown.d.cts +9 -0
- package/dist/markdown.d.ts +9 -0
- package/dist/markdown.js +234 -0
- package/dist/markdown.js.map +1 -0
- package/dist/renderer.cjs +503 -0
- package/dist/renderer.cjs.map +1 -0
- package/dist/renderer.d.cts +250 -0
- package/dist/renderer.d.ts +250 -0
- package/dist/renderer.js +501 -0
- package/dist/renderer.js.map +1 -0
- package/dist/rss.cjs +291 -0
- package/dist/rss.cjs.map +1 -0
- package/dist/rss.d.cts +158 -0
- package/dist/rss.d.ts +158 -0
- package/dist/rss.js +268 -0
- package/dist/rss.js.map +1 -0
- package/package.json +72 -6
package/README.md
CHANGED
|
@@ -1,904 +1,431 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<
|
|
3
|
-
<p align="center">
|
|
4
|
-
<strong>"New" badges that actually expire.</strong>
|
|
5
|
-
<br />
|
|
6
|
-
Lightweight feature discovery for SaaS sidebars, dashboards, and nav menus.
|
|
7
|
-
</p>
|
|
2
|
+
<img src="apps/docs/public/branding/icon-256.png" alt="FeatureDrop" width="72" height="72" />
|
|
8
3
|
</p>
|
|
9
4
|
|
|
5
|
+
<h1 align="center">FeatureDrop</h1>
|
|
6
|
+
|
|
10
7
|
<p align="center">
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<a href="https://github.com/GLINCKER/featuredrop/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/featuredrop?color=blue" alt="license"></a>
|
|
8
|
+
<strong>The open-source product adoption toolkit.</strong><br />
|
|
9
|
+
Changelogs • Tours • Checklists • Hotspots • Feedback — from your own codebase.<br />
|
|
10
|
+
< 3 kB core · Zero vendor lock-in · MIT licensed
|
|
15
11
|
</p>
|
|
16
12
|
|
|
17
13
|
<p align="center">
|
|
18
|
-
<a href="
|
|
19
|
-
<a href="
|
|
20
|
-
<a href="
|
|
21
|
-
<a href="https://
|
|
22
|
-
<a href="https://github.com/GLINCKER/featuredrop/blob/main/
|
|
14
|
+
<a href="https://github.com/GLINCKER/featuredrop/stargazers"><img src="https://img.shields.io/github/stars/GLINCKER/featuredrop?style=flat&color=f59e0b&label=stars" alt="GitHub stars" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/featuredrop"><img src="https://img.shields.io/npm/v/featuredrop?color=ea580c&label=npm" alt="npm version" /></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/featuredrop"><img src="https://img.shields.io/npm/dm/featuredrop?color=22c55e&label=downloads" alt="npm downloads" /></a>
|
|
17
|
+
<a href="https://bundlephobia.com/package/featuredrop"><img src="https://img.shields.io/bundlephobia/minzip/featuredrop?color=22c55e&label=gzipped" alt="bundle size" /></a>
|
|
18
|
+
<a href="https://github.com/GLINCKER/featuredrop/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/featuredrop?color=6366f1&label=license" alt="MIT license" /></a>
|
|
19
|
+
<a href="https://featuredrop.dev"><img src="https://img.shields.io/badge/docs-live-ea580c" alt="Live docs" /></a>
|
|
23
20
|
</p>
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://featuredrop.dev/docs/quickstart">Quickstart</a> •
|
|
24
|
+
<a href="https://featuredrop.dev/docs/components/gallery">Components</a> •
|
|
25
|
+
<a href="https://featuredrop.dev/playground">Playground</a> •
|
|
26
|
+
<a href="https://featuredrop.dev/docs/api">API Reference</a> •
|
|
27
|
+
<a href="#migration-from-beamer--pendo">Migration Guide</a>
|
|
28
|
+
</p>
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img src="apps/docs/public/og/github-social.png" alt="FeatureDrop — The open-source product adoption toolkit" width="720" />
|
|
32
|
+
</p>
|
|
28
33
|
|
|
29
|
-
Every SaaS ships features. Users miss them. You need "New" badges on sidebar items, but:
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
- **Beamer / Headway** — External widget, vendor lock-in, $59-299/mo.
|
|
33
|
-
- **Joyride / Shepherd.js** — Product tours, not persistent badges.
|
|
34
|
-
- **DIY** — You build it, forget expiry, badges stay forever. Users stop noticing.
|
|
35
|
+
---
|
|
35
36
|
|
|
36
|
-
##
|
|
37
|
+
## Why FeatureDrop?
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
Every SaaS ships features. Users miss them. The usual options are bad:
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
| Option | Problem |
|
|
42
|
+
|---|---|
|
|
43
|
+
| **Beamer / Headway / AnnounceKit** | External widget, vendor lock-in, $49–399/mo |
|
|
44
|
+
| **Pendo / Appcues** | Feature flags AND onboarding, ~$7k+/yr |
|
|
45
|
+
| **Joyride / Shepherd.js** | Tours only, not persistence or changelog |
|
|
46
|
+
| **DIY** | You build it, forget expiry, badges stay forever |
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
├──────────┬──────────────────────────────┤
|
|
48
|
-
│ │ │
|
|
49
|
-
│ Dashboard│ Welcome back, Sarah! │
|
|
50
|
-
│ │ │
|
|
51
|
-
│ Journal ●│ ┌─────────────────────┐ │
|
|
52
|
-
│ │ │ What's New (2) │ │
|
|
53
|
-
│ Analytics│ │ │ │
|
|
54
|
-
│ NEW │ │ ★ AI Journal │ │
|
|
55
|
-
│ │ │ Track decisions │ │
|
|
56
|
-
│ Billing │ │ with AI insights. │ │
|
|
57
|
-
│ │ │ [Try it →] │ │
|
|
58
|
-
│ Settings │ │ │ │
|
|
59
|
-
│ │ │ ★ Analytics v2 │ │
|
|
60
|
-
│ │ │ Real-time charts │ │
|
|
61
|
-
│ │ │ and CSV export. │ │
|
|
62
|
-
│ │ │ │ │
|
|
63
|
-
│ │ │ [Mark all as read] │ │
|
|
64
|
-
│ │ └─────────────────────┘ │
|
|
65
|
-
│ │ │
|
|
66
|
-
└──────────┴──────────────────────────────┘
|
|
67
|
-
|
|
68
|
-
● = dot badge NEW = pill badge (2) = count badge
|
|
69
|
-
```
|
|
48
|
+
**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.
|
|
49
|
+
|
|
50
|
+
---
|
|
70
51
|
|
|
71
52
|
## Quick Start
|
|
72
53
|
|
|
73
|
-
|
|
54
|
+
```bash
|
|
55
|
+
npm install featuredrop # < 3 kB core, zero runtime dependencies
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**1. Define your features:**
|
|
74
59
|
|
|
75
60
|
```ts
|
|
76
61
|
import { createManifest } from 'featuredrop'
|
|
77
62
|
|
|
78
|
-
export const
|
|
63
|
+
export const features = createManifest([
|
|
79
64
|
{
|
|
80
|
-
id: '
|
|
81
|
-
label: '
|
|
82
|
-
description: '
|
|
83
|
-
releasedAt: '2026-
|
|
84
|
-
showNewUntil: '2026-
|
|
85
|
-
sidebarKey: '/journal',
|
|
65
|
+
id: 'dark-mode',
|
|
66
|
+
label: 'Dark Mode',
|
|
67
|
+
description: 'Full dark theme support across every surface.',
|
|
68
|
+
releasedAt: '2026-03-01T00:00:00Z',
|
|
69
|
+
showNewUntil: '2026-04-01T00:00:00Z',
|
|
86
70
|
type: 'feature',
|
|
87
|
-
priority: '
|
|
88
|
-
cta: { label: 'Try it', url: '/
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
id: 'scheduled-reports',
|
|
92
|
-
label: 'Scheduled Reports',
|
|
93
|
-
releasedAt: '2026-02-23T00:00:00Z',
|
|
94
|
-
showNewUntil: '2026-03-23T00:00:00Z',
|
|
95
|
-
dependsOn: { clicked: ['ai-journal'] }, // progressive rollout
|
|
96
|
-
trigger: { type: 'page', match: '/reports/*' }, // contextual trigger
|
|
71
|
+
priority: 'high',
|
|
72
|
+
cta: { label: 'Try it', url: '/settings/appearance' },
|
|
97
73
|
},
|
|
98
74
|
])
|
|
99
75
|
```
|
|
100
76
|
|
|
101
|
-
**2.
|
|
77
|
+
**2. Wrap your app:**
|
|
102
78
|
|
|
103
|
-
```
|
|
79
|
+
```tsx
|
|
80
|
+
import { FeatureDropProvider } from 'featuredrop/react'
|
|
104
81
|
import { LocalStorageAdapter } from 'featuredrop'
|
|
82
|
+
import { features } from './features'
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
watermark: user.featuresSeenAt, // from your server
|
|
108
|
-
onDismissAll: (now) => api.markFeaturesSeen(now), // optional server sync
|
|
109
|
-
})
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
For offline-heavy apps, use `IndexedDBAdapter` to persist the same state with IndexedDB + local fallback.
|
|
113
|
-
It also supports queued dismiss sync (`flushQueue`) + remote state reconciliation (`syncFromRemote`) for offline-first flows.
|
|
114
|
-
For remote/state sync, `RemoteAdapter` now includes retry + circuit-breaker protection and a monitoring-friendly `isHealthy()` check.
|
|
115
|
-
|
|
116
|
-
**3. Check what's new:**
|
|
117
|
-
|
|
118
|
-
```ts
|
|
119
|
-
import { getNewFeatures, hasNewFeature } from 'featuredrop'
|
|
120
|
-
|
|
121
|
-
const newFeatures = getNewFeatures(FEATURES, storage)
|
|
122
|
-
hasNewFeature(FEATURES, '/journal', storage) // true/false
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Works with **any framework**. Zero React dependency for vanilla use.
|
|
126
|
-
|
|
127
|
-
## Adoption Analytics
|
|
128
|
-
|
|
129
|
-
Pipe structured adoption events to PostHog/Amplitude/Mixpanel/Segment or your own endpoint.
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
import { AnalyticsCollector, PostHogAdapter } from 'featuredrop'
|
|
133
|
-
|
|
134
|
-
const collector = new AnalyticsCollector({
|
|
135
|
-
adapter: new PostHogAdapter(posthog),
|
|
136
|
-
batchSize: 20,
|
|
137
|
-
flushInterval: 10_000,
|
|
138
|
-
sampleRate: 1,
|
|
139
|
-
})
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
<FeatureDropProvider
|
|
144
|
-
manifest={FEATURES}
|
|
145
|
-
storage={storage}
|
|
146
|
-
collector={collector}
|
|
147
|
-
>
|
|
84
|
+
<FeatureDropProvider manifest={features} storage={new LocalStorageAdapter()}>
|
|
148
85
|
<App />
|
|
149
86
|
</FeatureDropProvider>
|
|
150
87
|
```
|
|
151
88
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Run deterministic per-user announcement variants with weighted splits.
|
|
155
|
-
|
|
156
|
-
```ts
|
|
157
|
-
{
|
|
158
|
-
id: 'ai-journal',
|
|
159
|
-
label: 'AI Decision Journal',
|
|
160
|
-
variants: {
|
|
161
|
-
control: { description: 'Track decisions with AI-powered insights.' },
|
|
162
|
-
treatment: { description: 'Never second-guess decisions again.' },
|
|
163
|
-
},
|
|
164
|
-
variantSplit: [50, 50],
|
|
165
|
-
}
|
|
166
|
-
```
|
|
89
|
+
**3. Add badges and a changelog:**
|
|
167
90
|
|
|
168
91
|
```tsx
|
|
169
|
-
|
|
170
|
-
manifest={FEATURES}
|
|
171
|
-
storage={storage}
|
|
172
|
-
variantKey={user.id} // stable key for deterministic assignment
|
|
173
|
-
>
|
|
174
|
-
<App />
|
|
175
|
-
</FeatureDropProvider>
|
|
176
|
-
```
|
|
92
|
+
import { NewBadge, ChangelogWidget } from 'featuredrop/react'
|
|
177
93
|
|
|
178
|
-
|
|
94
|
+
// Sidebar nav item
|
|
95
|
+
<a href="/settings">
|
|
96
|
+
Settings <NewBadge id="dark-mode" /> {/* auto-expires */}
|
|
97
|
+
</a>
|
|
179
98
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
```tsx
|
|
183
|
-
import { ThemeProvider, ChangelogWidget } from 'featuredrop/react'
|
|
184
|
-
|
|
185
|
-
<ThemeProvider theme="dark">
|
|
186
|
-
<ChangelogWidget />
|
|
187
|
-
</ThemeProvider>
|
|
99
|
+
// Changelog button
|
|
100
|
+
<ChangelogWidget title="What's new" showReactions />
|
|
188
101
|
```
|
|
189
102
|
|
|
190
|
-
|
|
191
|
-
import { createTheme } from 'featuredrop'
|
|
192
|
-
import { ThemeProvider, ChangelogPage } from 'featuredrop/react'
|
|
103
|
+
That's it. Badges expire on schedule. No database setup. No vendor account. No tracking pixels.
|
|
193
104
|
|
|
194
|
-
|
|
195
|
-
colors: { primary: '#7c3aed' },
|
|
196
|
-
radii: { lg: '16px' },
|
|
197
|
-
})
|
|
105
|
+
→ **Full walkthrough:** [10-minute quickstart](https://featuredrop.dev/docs/quickstart)
|
|
198
106
|
|
|
199
|
-
|
|
200
|
-
<ChangelogPage />
|
|
201
|
-
</ThemeProvider>
|
|
202
|
-
```
|
|
107
|
+
---
|
|
203
108
|
|
|
204
|
-
|
|
205
|
-
You can also pass `theme` directly to `ChangelogWidget` and `ChangelogPage` for component-scoped overrides.
|
|
109
|
+
## Components
|
|
206
110
|
|
|
207
|
-
|
|
111
|
+
Everything you'd get from Beamer or Pendo, but free, self-hosted, and headless-first.
|
|
112
|
+
|
|
113
|
+
| Component | Description |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `<ChangelogWidget>` | Trigger button + slide-out/modal changelog with emoji reactions |
|
|
116
|
+
| `<ChangelogPage>` | Full-page changelog with filters, search, and pagination |
|
|
117
|
+
| `<NewBadge>` | Auto-expiring pill / dot / count badge |
|
|
118
|
+
| `<Banner>` | Top-of-page or inline banner with `announcement`, `warning`, `info` variants |
|
|
119
|
+
| `<Toast>` | Stackable toast notifications with auto-dismiss and position control |
|
|
120
|
+
| `<Tour>` | Multi-step guided product tours with keyboard nav and persistence |
|
|
121
|
+
| `<Checklist>` | Onboarding task checklists with progress tracking |
|
|
122
|
+
| `<Spotlight>` | Pulsing DOM-attached beacon/tooltip |
|
|
123
|
+
| `<SpotlightChain>` | Chained spotlight walkthrough ("here are 3 new things") |
|
|
124
|
+
| `<AnnouncementModal>` | Priority-gated modal with optional image carousel |
|
|
125
|
+
| `<Hotspot>` / `<TooltipGroup>` | Persistent contextual hints with visibility caps |
|
|
126
|
+
| `<FeedbackWidget>` | In-app feedback with category, emoji, screenshot support |
|
|
127
|
+
| `<Survey>` | NPS / CSAT / CES / custom survey engine with trigger rules |
|
|
128
|
+
| `<FeatureRequestButton>` | Per-feature voting button with vote guard |
|
|
129
|
+
| `<FeatureRequestForm>` | Request capture + sortable request list |
|
|
130
|
+
|
|
131
|
+
All components are headless-capable via render props. [See live demos →](https://featuredrop.dev/docs/components/gallery)
|
|
208
132
|
|
|
209
|
-
|
|
133
|
+
---
|
|
210
134
|
|
|
211
|
-
|
|
212
|
-
<FeatureDropProvider
|
|
213
|
-
manifest={FEATURES}
|
|
214
|
-
storage={storage}
|
|
215
|
-
locale="fr"
|
|
216
|
-
translations={{
|
|
217
|
-
submit: 'Envoyer maintenant',
|
|
218
|
-
}}
|
|
219
|
-
>
|
|
220
|
-
<App />
|
|
221
|
-
</FeatureDropProvider>
|
|
222
|
-
```
|
|
135
|
+
## How It Works
|
|
223
136
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
137
|
+
```
|
|
138
|
+
Manifest (static) Storage (runtime)
|
|
139
|
+
┌─────────────────────┐ ┌──────────────────────┐
|
|
140
|
+
│ releasedAt: Mar 1 │ │ watermark ← server │
|
|
141
|
+
│ showNewUntil: Apr 1 │ │ dismissed ← localStorage │
|
|
142
|
+
└──────────┬──────────┘ └──────────┬───────────┘
|
|
143
|
+
│ │
|
|
144
|
+
└──────────┐ ┌─────────────────┘
|
|
145
|
+
▼ ▼
|
|
146
|
+
┌───────────────┐
|
|
147
|
+
│ isNew() │
|
|
148
|
+
│ │
|
|
149
|
+
│ !dismissed │
|
|
150
|
+
│ !expired │
|
|
151
|
+
│ afterWatermark│
|
|
152
|
+
└───────┬───────┘
|
|
153
|
+
│
|
|
154
|
+
true / false
|
|
155
|
+
```
|
|
227
156
|
|
|
228
|
-
|
|
157
|
+
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.
|
|
229
158
|
|
|
230
|
-
|
|
159
|
+
Read the full [Architecture doc](https://featuredrop.dev/docs/concepts/architecture) for cross-device sync and custom adapter patterns.
|
|
231
160
|
|
|
232
|
-
|
|
233
|
-
<FeatureDropProvider manifest={FEATURES} storage={storage} animation="subtle">
|
|
234
|
-
<App />
|
|
235
|
-
</FeatureDropProvider>
|
|
236
|
-
```
|
|
161
|
+
---
|
|
237
162
|
|
|
238
|
-
|
|
239
|
-
Reduced-motion users automatically receive `none`.
|
|
240
|
-
Built-in `ChangelogWidget`, `AnnouncementModal`, and `Toast` include enter/exit animations, and `Tour`, `Survey`, `FeedbackWidget`, `Spotlight`, `SpotlightChain`, and `Hotspot` follow the same preset for enter/pulse motion.
|
|
163
|
+
## Storage Adapters
|
|
241
164
|
|
|
242
|
-
|
|
165
|
+
| Adapter | Import | Best For |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `LocalStorageAdapter` | `featuredrop` | Browser apps (default) |
|
|
168
|
+
| `MemoryAdapter` | `featuredrop` | Testing, SSR |
|
|
169
|
+
| `IndexedDBAdapter` | `featuredrop/adapters` | Offline-first PWAs |
|
|
170
|
+
| `RemoteAdapter` | `featuredrop/adapters` | Server-backed with retry + circuit-breaker |
|
|
171
|
+
| `HybridAdapter` | `featuredrop/adapters` | Local + remote with batched flush |
|
|
172
|
+
| Redis / PostgreSQL / DynamoDB | `featuredrop/adapters` | Database-backed server-side apps |
|
|
243
173
|
|
|
244
|
-
|
|
174
|
+
[All adapters →](https://featuredrop.dev/docs/adapters/overview)
|
|
245
175
|
|
|
246
|
-
|
|
247
|
-
import { createChangelogRenderer } from 'featuredrop'
|
|
176
|
+
---
|
|
248
177
|
|
|
249
|
-
|
|
250
|
-
renderer.subscribe((state) => console.log(state.newCount))
|
|
251
|
-
renderer.actions.dismiss('ai-journal')
|
|
252
|
-
```
|
|
178
|
+
## CLI
|
|
253
179
|
|
|
254
|
-
|
|
180
|
+
Manage your manifest from the command line:
|
|
255
181
|
|
|
256
|
-
|
|
182
|
+
```bash
|
|
183
|
+
# Scaffold
|
|
184
|
+
npx featuredrop init
|
|
185
|
+
npx featuredrop add --label "Dark Mode" --category ui --type feature
|
|
257
186
|
|
|
258
|
-
|
|
187
|
+
# Validate & audit
|
|
188
|
+
npx featuredrop validate # schema + duplicate ID check
|
|
189
|
+
npx featuredrop doctor # security + best practice audit
|
|
190
|
+
npx featuredrop stats # manifest summary stats
|
|
259
191
|
|
|
260
|
-
|
|
192
|
+
# Build (markdown → JSON)
|
|
193
|
+
npx featuredrop build --pattern "features/**/*.md" --out featuredrop.manifest.json
|
|
261
194
|
|
|
262
|
-
|
|
195
|
+
# Generate outputs
|
|
196
|
+
npx featuredrop generate-rss --out featuredrop.rss.xml
|
|
197
|
+
npx featuredrop generate-changelog --out CHANGELOG.generated.md
|
|
263
198
|
|
|
264
|
-
|
|
265
|
-
npx featuredrop
|
|
266
|
-
npx featuredrop
|
|
267
|
-
npx featuredrop
|
|
268
|
-
npx featuredrop
|
|
269
|
-
npx featuredrop
|
|
270
|
-
npx featuredrop doctor --pattern "features/**/*.md"
|
|
271
|
-
npx featuredrop generate-rss --pattern "features/**/*.md" --out featuredrop.rss.xml
|
|
272
|
-
npx featuredrop generate-changelog --pattern "features/**/*.md" --out CHANGELOG.generated.md
|
|
273
|
-
npx featuredrop migrate --from beamer --input beamer-export.json --out featuredrop.manifest.json
|
|
274
|
-
npx featuredrop migrate --from headway --input headway-export.json --out featuredrop.manifest.json
|
|
275
|
-
npx featuredrop migrate --from announcekit --input announcekit-export.json --out featuredrop.manifest.json
|
|
276
|
-
npx featuredrop migrate --from canny --input canny-export.json --out featuredrop.manifest.json
|
|
277
|
-
npx featuredrop migrate --from launchnotes --input launchnotes-export.json --out featuredrop.manifest.json
|
|
199
|
+
# Migrate from vendors
|
|
200
|
+
npx featuredrop migrate --from beamer --input beamer-export.json
|
|
201
|
+
npx featuredrop migrate --from headway --input headway-export.json
|
|
202
|
+
npx featuredrop migrate --from announcekit --input announcekit-export.json
|
|
203
|
+
npx featuredrop migrate --from canny --input canny-export.json
|
|
204
|
+
npx featuredrop migrate --from launchnotes --input launchnotes-export.json
|
|
278
205
|
```
|
|
279
206
|
|
|
280
|
-
|
|
207
|
+
[CLI reference →](https://featuredrop.dev/docs/automation/ci)
|
|
281
208
|
|
|
282
|
-
```md
|
|
283
|
-
---
|
|
284
|
-
id: ai-journal
|
|
285
|
-
label: AI Journal
|
|
286
|
-
type: feature
|
|
287
|
-
category: ai
|
|
288
|
-
releasedAt: 2026-02-15T00:00:00Z
|
|
289
|
-
showNewUntil: 2026-03-15T00:00:00Z
|
|
290
|
-
cta:
|
|
291
|
-
label: Try it now
|
|
292
|
-
url: /journal
|
|
293
209
|
---
|
|
294
|
-
Track decisions and outcomes with AI-powered insights.
|
|
295
|
-
```
|
|
296
210
|
|
|
297
|
-
##
|
|
211
|
+
## Framework Adapters
|
|
298
212
|
|
|
299
|
-
|
|
213
|
+
| Framework | Status | Import |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| React / Next.js | ✅ Stable | `featuredrop/react` |
|
|
216
|
+
| Vanilla JS | ✅ Stable | `featuredrop` |
|
|
217
|
+
| SolidJS | 🔬 Preview | `featuredrop/solid` |
|
|
218
|
+
| Preact | 🔬 Preview | `featuredrop/preact` |
|
|
219
|
+
| Web Components | 🔬 Preview | `featuredrop/web-components` |
|
|
220
|
+
| Angular | 🔬 Preview | `featuredrop/angular` |
|
|
221
|
+
| Vue 3 | 🔬 Preview | `featuredrop/vue` |
|
|
222
|
+
| Svelte 5 | 🔬 Preview | `featuredrop/svelte` |
|
|
300
223
|
|
|
301
|
-
|
|
302
|
-
import { ContentfulAdapter, SanityAdapter, StrapiAdapter, NotionAdapter, MarkdownAdapter } from 'featuredrop'
|
|
224
|
+
---
|
|
303
225
|
|
|
304
|
-
|
|
305
|
-
spaceId: process.env.CONTENTFUL_SPACE!,
|
|
306
|
-
accessToken: process.env.CONTENTFUL_TOKEN!,
|
|
307
|
-
contentType: 'featureRelease',
|
|
308
|
-
})
|
|
226
|
+
## React Hooks
|
|
309
227
|
|
|
310
|
-
|
|
311
|
-
|
|
228
|
+
| Hook | Returns |
|
|
229
|
+
|---|---|
|
|
230
|
+
| `useFeatureDrop()` | Full context: features, count, dismiss, throttle controls |
|
|
231
|
+
| `useNewFeature(key)` | Single nav item: `{ isNew, feature, dismiss }` |
|
|
232
|
+
| `useNewCount()` | Current unread badge count |
|
|
233
|
+
| `useTour(id)` | Imperative tour controls and step snapshot |
|
|
234
|
+
| `useTourSequencer(sequence)` | Ordered multi-tour orchestration |
|
|
235
|
+
| `useChecklist(id)` | Checklist progress + task controls |
|
|
236
|
+
| `useSurvey(id)` | Survey controls: `show`, `hide`, `askLater` |
|
|
237
|
+
| `useTabNotification()` | Browser tab title count: `"(3) My App"` |
|
|
312
238
|
|
|
313
|
-
|
|
239
|
+
---
|
|
314
240
|
|
|
315
241
|
## Notification Bridges
|
|
316
242
|
|
|
317
|
-
|
|
243
|
+
Fan out release notifications to Slack, Discord, email, webhooks, or RSS on deploy:
|
|
318
244
|
|
|
319
245
|
```ts
|
|
320
|
-
import {
|
|
321
|
-
SlackBridge,
|
|
322
|
-
DiscordBridge,
|
|
323
|
-
EmailDigestGenerator,
|
|
324
|
-
WebhookBridge,
|
|
325
|
-
RSSFeedGenerator,
|
|
326
|
-
} from 'featuredrop/bridges'
|
|
246
|
+
import { SlackBridge, DiscordBridge, WebhookBridge, EmailDigestGenerator, RSSFeedGenerator } from 'featuredrop/bridges'
|
|
327
247
|
|
|
328
248
|
await SlackBridge.notify(feature, { webhookUrl: process.env.SLACK_WEBHOOK! })
|
|
329
249
|
await DiscordBridge.notify(feature, { webhookUrl: process.env.DISCORD_WEBHOOK! })
|
|
330
250
|
await WebhookBridge.post(feature, { url: 'https://api.example.com/hooks/features' })
|
|
331
251
|
|
|
332
|
-
const html = EmailDigestGenerator.generate(features, { title: 'Weekly
|
|
333
|
-
const rss
|
|
252
|
+
const html = EmailDigestGenerator.generate(features, { title: 'Weekly Product Updates' })
|
|
253
|
+
const rss = RSSFeedGenerator.generate(features, { title: 'Product Updates' })
|
|
334
254
|
```
|
|
335
255
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
Use `featuredrop/ci` for manifest diffing in pull-request checks:
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
import { diffManifest, generateChangelogDiff, validateManifestForCI } from 'featuredrop/ci'
|
|
256
|
+
---
|
|
342
257
|
|
|
343
|
-
|
|
344
|
-
const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
|
|
345
|
-
const validation = validateManifestForCI(afterManifest)
|
|
346
|
-
```
|
|
258
|
+
## Analytics
|
|
347
259
|
|
|
348
|
-
|
|
260
|
+
Pipe adoption events to any analytics provider:
|
|
349
261
|
|
|
350
|
-
```
|
|
351
|
-
|
|
262
|
+
```tsx
|
|
263
|
+
<FeatureDropProvider
|
|
264
|
+
manifest={features}
|
|
265
|
+
storage={storage}
|
|
266
|
+
analytics={{
|
|
267
|
+
onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
|
|
268
|
+
onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
|
|
269
|
+
onFeatureClicked: (f) => posthog.capture('feature_clicked', { id: f.id }),
|
|
270
|
+
onWidgetOpened: () => posthog.capture('changelog_opened'),
|
|
271
|
+
}}
|
|
272
|
+
>
|
|
273
|
+
<App />
|
|
274
|
+
</FeatureDropProvider>
|
|
352
275
|
```
|
|
353
276
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
Gate announcement visibility behind flags via `feature.flagKey`:
|
|
357
|
-
|
|
358
|
-
```ts
|
|
359
|
-
import { createFlagBridge, LaunchDarklyBridge, PostHogBridge } from 'featuredrop/flags'
|
|
277
|
+
Works with PostHog, Mixpanel, Amplitude, Segment, or any custom endpoint.
|
|
360
278
|
|
|
361
|
-
|
|
362
|
-
isEnabled: (flagKey) => myFlagService.isOn(flagKey),
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
// Provider + feature entry
|
|
366
|
-
<FeatureDropProvider manifest={FEATURES} storage={storage} flagBridge={bridge} />
|
|
367
|
-
// { id: 'ai-journal', flagKey: 'ai-journal-enabled', ... }
|
|
368
|
-
|
|
369
|
-
// Or vendor bridges
|
|
370
|
-
const ld = new LaunchDarklyBridge(ldClient)
|
|
371
|
-
const ph = new PostHogBridge(posthog)
|
|
372
|
-
```
|
|
279
|
+
---
|
|
373
280
|
|
|
374
|
-
##
|
|
281
|
+
## User Segmentation
|
|
375
282
|
|
|
376
|
-
|
|
283
|
+
Show the right features to the right users:
|
|
377
284
|
|
|
378
285
|
```tsx
|
|
379
|
-
<FeatureDropProvider
|
|
380
|
-
|
|
286
|
+
<FeatureDropProvider
|
|
287
|
+
manifest={features}
|
|
288
|
+
storage={storage}
|
|
289
|
+
userContext={{ plan: 'pro', role: 'admin', region: 'eu' }}
|
|
290
|
+
>
|
|
291
|
+
<App />
|
|
292
|
+
</FeatureDropProvider>
|
|
381
293
|
```
|
|
382
294
|
|
|
383
|
-
|
|
295
|
+
Define audience rules per feature in your manifest:
|
|
384
296
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
PreviewPanel,
|
|
392
|
-
AudienceBuilder,
|
|
393
|
-
} from 'featuredrop/admin'
|
|
394
|
-
|
|
395
|
-
<ManifestEditor features={features} onSave={saveManifest} />
|
|
396
|
-
<ScheduleCalendar features={features} onSchedule={scheduleFeature} />
|
|
397
|
-
<PreviewPanel feature={selectedFeature} components={['badge', 'changelog', 'toast']} />
|
|
398
|
-
<AudienceBuilder segments={['free', 'pro']} roles={['admin', 'viewer']} onSave={saveAudience} />
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"id": "ai-copilot",
|
|
300
|
+
"label": "AI Copilot",
|
|
301
|
+
"audience": { "plan": ["pro", "enterprise"], "region": ["us", "eu"] }
|
|
302
|
+
}
|
|
399
303
|
```
|
|
400
304
|
|
|
401
|
-
|
|
305
|
+
Users outside the audience never see the feature. No server calls. No feature flag service needed.
|
|
402
306
|
|
|
403
|
-
|
|
307
|
+
---
|
|
404
308
|
|
|
405
|
-
##
|
|
309
|
+
## CI Integration
|
|
406
310
|
|
|
407
|
-
Validate manifest
|
|
311
|
+
Validate your manifest in every pull request:
|
|
408
312
|
|
|
409
313
|
```ts
|
|
410
314
|
import {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
const result = validateManifest(data)
|
|
424
|
-
if (!result.valid) {
|
|
425
|
-
throw new Error(result.errors.map((e) => `${e.path}: ${e.message}`).join("; "))
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
console.log(featureEntryJsonSchema.properties.id.type) // "string"
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
## Testing Utilities
|
|
432
|
-
|
|
433
|
-
Use `featuredrop/testing` to speed up unit and component tests.
|
|
434
|
-
|
|
435
|
-
```tsx
|
|
436
|
-
import { render, screen } from '@testing-library/react'
|
|
437
|
-
import { useNewCount } from 'featuredrop/react'
|
|
438
|
-
import { createMockManifest, createMockStorage, createTestProvider } from 'featuredrop/testing'
|
|
439
|
-
|
|
440
|
-
const manifest = createMockManifest([{ label: 'AI Journal', releasedAt: 'today', showNewUntil: '+14d' }])
|
|
441
|
-
const storage = createMockStorage()
|
|
442
|
-
const Wrapper = createTestProvider({ manifest, storage })
|
|
443
|
-
|
|
444
|
-
function Count() {
|
|
445
|
-
return <span>{useNewCount()}</span>
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
render(<Count />, { wrapper: Wrapper })
|
|
449
|
-
expect(screen.getByText('1')).toBeInTheDocument()
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
## Playground & Online Demos
|
|
453
|
-
|
|
454
|
-
Use the lightweight component playground for quick UI iteration:
|
|
455
|
-
|
|
456
|
-
```bash
|
|
457
|
-
pnpm --dir examples/sandbox-react install
|
|
458
|
-
pnpm playground
|
|
459
|
-
pnpm playground:build
|
|
315
|
+
diffManifest,
|
|
316
|
+
generateChangelogDiff,
|
|
317
|
+
generateChangelogDiffMarkdown,
|
|
318
|
+
validateManifestForCI
|
|
319
|
+
} from 'featuredrop/ci'
|
|
320
|
+
|
|
321
|
+
const diff = diffManifest(beforeManifest, afterManifest)
|
|
322
|
+
const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
|
|
323
|
+
const markdown = generateChangelogDiffMarkdown(diff, { includeFieldChanges: true })
|
|
324
|
+
const validation = validateManifestForCI(afterManifest)
|
|
460
325
|
```
|
|
461
326
|
|
|
462
|
-
One-click editable demos:
|
|
463
|
-
|
|
464
|
-
- React sandbox source: `examples/sandbox-react`
|
|
465
|
-
- StackBlitz: https://stackblitz.com/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
|
|
466
|
-
- CodeSandbox: https://codesandbox.io/p/sandbox/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
|
|
467
|
-
|
|
468
|
-
## Docs Site (Nextra + Vercel)
|
|
469
|
-
|
|
470
|
-
The repo now includes a docs app scaffold at `apps/docs` for a fast validation launch on Vercel.
|
|
471
|
-
|
|
472
327
|
```bash
|
|
473
|
-
pnpm
|
|
474
|
-
pnpm docs:dev
|
|
475
|
-
pnpm docs:build
|
|
328
|
+
pnpm size-check # bundle budget check post-build
|
|
476
329
|
```
|
|
477
330
|
|
|
478
|
-
|
|
331
|
+
[CI setup guide →](https://featuredrop.dev/docs/automation/ci)
|
|
479
332
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
Everything you'd expect from Beamer or Headway — but free, self-hosted, and headless-first.
|
|
483
|
-
|
|
484
|
-
### Changelog Widget
|
|
485
|
-
|
|
486
|
-
The #1 feature people install these tools for. Trigger button with unread count badge, slide-out panel with rich changelog feed.
|
|
487
|
-
|
|
488
|
-
```tsx
|
|
489
|
-
import { ChangelogWidget } from 'featuredrop/react'
|
|
490
|
-
|
|
491
|
-
// Default: slide-out panel with all features
|
|
492
|
-
<ChangelogWidget />
|
|
493
|
-
|
|
494
|
-
// Or modal / popover variant
|
|
495
|
-
<ChangelogWidget variant="modal" title="Release Notes" />
|
|
496
|
-
|
|
497
|
-
// Enable emoji reactions on entries
|
|
498
|
-
<ChangelogWidget showReactions />
|
|
499
|
-
|
|
500
|
-
// Fully headless
|
|
501
|
-
<ChangelogWidget>
|
|
502
|
-
{({ isOpen, toggle, features, count, dismissAll }) => (
|
|
503
|
-
<YourCustomUI />
|
|
504
|
-
)}
|
|
505
|
-
</ChangelogWidget>
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### Spotlight Beacon
|
|
509
|
-
|
|
510
|
-
Pulsing beacon that attaches to any DOM element. Click to see feature tooltip.
|
|
511
|
-
|
|
512
|
-
```tsx
|
|
513
|
-
import { Spotlight } from 'featuredrop/react'
|
|
514
|
-
|
|
515
|
-
const ref = useRef<HTMLButtonElement>(null)
|
|
516
|
-
<button ref={ref}>Analytics</button>
|
|
517
|
-
<Spotlight featureId="analytics-v2" targetRef={ref} />
|
|
518
|
-
|
|
519
|
-
// Or with CSS selector
|
|
520
|
-
<Spotlight featureId="analytics-v2" targetSelector="#analytics-btn" />
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### Announcement Banner
|
|
524
|
-
|
|
525
|
-
Top-of-page or inline banner for major announcements. Auto-expires like badges.
|
|
526
|
-
|
|
527
|
-
```tsx
|
|
528
|
-
import { Banner } from 'featuredrop/react'
|
|
529
|
-
|
|
530
|
-
<Banner featureId="v2-launch" variant="announcement" />
|
|
531
|
-
<Banner featureId="breaking-change" variant="warning" />
|
|
532
|
-
<Banner featureId="security-fix" variant="info" position="fixed" />
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### Toast Notifications
|
|
536
|
-
|
|
537
|
-
Brief popup notifications for new features. Auto-dismiss, stackable, configurable position.
|
|
538
|
-
|
|
539
|
-
```tsx
|
|
540
|
-
import { Toast } from 'featuredrop/react'
|
|
541
|
-
|
|
542
|
-
<Toast position="bottom-right" maxVisible={3} />
|
|
543
|
-
|
|
544
|
-
// Specific features only
|
|
545
|
-
<Toast featureIds={["ai-journal"]} autoDismissMs={5000} />
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Product Tours
|
|
549
|
-
|
|
550
|
-
Guided, multi-step onboarding with keyboard navigation and persistence.
|
|
551
|
-
|
|
552
|
-
```tsx
|
|
553
|
-
import { Tour, useTour } from 'featuredrop/react'
|
|
554
|
-
|
|
555
|
-
<Tour id="onboarding" steps={steps} />
|
|
556
|
-
const { startTour, nextStep, skipTour } = useTour('onboarding')
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### Onboarding Checklist
|
|
560
|
-
|
|
561
|
-
Task-based onboarding that can trigger tours, links, or callbacks.
|
|
562
|
-
|
|
563
|
-
```tsx
|
|
564
|
-
import { Checklist, useChecklist } from 'featuredrop/react'
|
|
565
|
-
|
|
566
|
-
<Checklist id="getting-started" tasks={tasks} />
|
|
567
|
-
const { completeTask, progress } = useChecklist('getting-started')
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
### Feedback Widget
|
|
571
|
-
|
|
572
|
-
Collect lightweight in-app feedback with optional emoji and screenshots.
|
|
573
|
-
|
|
574
|
-
```tsx
|
|
575
|
-
import { FeedbackWidget } from 'featuredrop/react'
|
|
576
|
-
|
|
577
|
-
<FeedbackWidget
|
|
578
|
-
featureId="ai-journal"
|
|
579
|
-
onSubmit={async (payload) => {
|
|
580
|
-
await fetch('/api/feedback', { method: 'POST', body: JSON.stringify(payload) })
|
|
581
|
-
}}
|
|
582
|
-
showEmoji
|
|
583
|
-
showScreenshot
|
|
584
|
-
rateLimit="1-per-feature"
|
|
585
|
-
/>
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
### Survey (NPS / CSAT / CES / Custom)
|
|
589
|
-
|
|
590
|
-
Run micro-surveys with trigger rules and imperative controls.
|
|
591
|
-
|
|
592
|
-
```tsx
|
|
593
|
-
import { Survey, useSurvey } from 'featuredrop/react'
|
|
594
|
-
|
|
595
|
-
<Survey
|
|
596
|
-
id="nps-main"
|
|
597
|
-
type="nps"
|
|
598
|
-
prompt="How likely are you to recommend us?"
|
|
599
|
-
triggerRules={{ minDaysSinceSignup: 7, signupAt: user.createdAt }}
|
|
600
|
-
onSubmit={saveSurvey}
|
|
601
|
-
/>
|
|
602
|
-
|
|
603
|
-
const { show } = useSurvey('nps-main')
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
### Feature Request Voting
|
|
607
|
-
|
|
608
|
-
Capture and rank product requests with local persistence and optional webhook sync.
|
|
609
|
-
|
|
610
|
-
```tsx
|
|
611
|
-
import { FeatureRequestButton, FeatureRequestForm } from 'featuredrop/react'
|
|
612
|
-
|
|
613
|
-
<FeatureRequestButton featureId="dark-mode" requestTitle="Dark mode" />
|
|
614
|
-
|
|
615
|
-
<FeatureRequestForm
|
|
616
|
-
categories={['UI', 'Performance', 'Integration', 'Other']}
|
|
617
|
-
onSubmit={async (request) => {
|
|
618
|
-
await fetch('/api/requests', { method: 'POST', body: JSON.stringify(request) })
|
|
619
|
-
}}
|
|
620
|
-
/>
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
### Hotspots & Tooltips
|
|
624
|
-
|
|
625
|
-
Persistent contextual hints attached to specific UI targets.
|
|
626
|
-
|
|
627
|
-
```tsx
|
|
628
|
-
import { Hotspot, TooltipGroup } from 'featuredrop/react'
|
|
629
|
-
|
|
630
|
-
<TooltipGroup maxVisible={1}>
|
|
631
|
-
<Hotspot id="export-help" target="#export-btn" frequency="once">
|
|
632
|
-
Export supports CSV, PDF, and Excel.
|
|
633
|
-
</Hotspot>
|
|
634
|
-
</TooltipGroup>
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
### Announcement Modal
|
|
638
|
-
|
|
639
|
-
Priority-based modal announcements with optional slide carousel.
|
|
640
|
-
|
|
641
|
-
```tsx
|
|
642
|
-
import { AnnouncementModal } from 'featuredrop/react'
|
|
643
|
-
|
|
644
|
-
<AnnouncementModal
|
|
645
|
-
feature={criticalFeature}
|
|
646
|
-
trigger="auto"
|
|
647
|
-
frequency="once"
|
|
648
|
-
/>
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
### Spotlight Chain
|
|
652
|
-
|
|
653
|
-
Lightweight chained spotlights for "here are 3 new things" flows.
|
|
654
|
-
|
|
655
|
-
```tsx
|
|
656
|
-
import { SpotlightChain } from 'featuredrop/react'
|
|
657
|
-
|
|
658
|
-
<SpotlightChain
|
|
659
|
-
steps={[
|
|
660
|
-
{ target: '#sidebar', content: 'New navigation' },
|
|
661
|
-
{ target: '#search', content: 'Global search' },
|
|
662
|
-
]}
|
|
663
|
-
/>
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
### NewBadge
|
|
667
|
-
|
|
668
|
-
Headless badge component with variants. Zero CSS framework dependency.
|
|
669
|
-
|
|
670
|
-
```tsx
|
|
671
|
-
import { NewBadge } from 'featuredrop/react'
|
|
672
|
-
|
|
673
|
-
<NewBadge /> // "New" pill
|
|
674
|
-
<NewBadge variant="dot" /> // Pulsing dot
|
|
675
|
-
<NewBadge variant="count" count={3} /> // Count badge
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
### Tab Title Notification
|
|
679
|
-
|
|
680
|
-
Updates the browser tab title with unread count. Restores when all read.
|
|
681
|
-
|
|
682
|
-
```tsx
|
|
683
|
-
import { useTabNotification } from 'featuredrop/react'
|
|
684
|
-
|
|
685
|
-
useTabNotification() // "(3) My App"
|
|
686
|
-
useTabNotification({ template: "[{count} new] {title}", flash: true })
|
|
687
|
-
```
|
|
333
|
+
---
|
|
688
334
|
|
|
689
|
-
##
|
|
335
|
+
## Migration from Beamer / Pendo
|
|
690
336
|
|
|
691
337
|
```bash
|
|
692
|
-
|
|
338
|
+
npx featuredrop migrate --from beamer --input beamer-export.json --out features.json
|
|
693
339
|
```
|
|
694
340
|
|
|
695
|
-
**
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
</FeatureDropProvider>
|
|
703
|
-
```
|
|
341
|
+
| | Beamer | Pendo | **FeatureDrop** |
|
|
342
|
+
|---|---|---|---|
|
|
343
|
+
| Price | $59–399/mo | $7k+/yr | **Free forever** |
|
|
344
|
+
| Bundle impact | External script | ~300 kB agent | **< 3 kB core** |
|
|
345
|
+
| Vendor lock-in | Yes | Yes | **No** |
|
|
346
|
+
| Data ownership | Vendor-hosted | Vendor-hosted | **Your repo** |
|
|
347
|
+
| Customization | CSS themes | Limited | **Full source access** |
|
|
704
348
|
|
|
705
|
-
|
|
349
|
+
[Full migration guide →](https://featuredrop.dev/docs/migration)
|
|
706
350
|
|
|
707
|
-
|
|
708
|
-
<FeatureDropProvider
|
|
709
|
-
manifest={FEATURES}
|
|
710
|
-
storage={storage}
|
|
711
|
-
throttle={{
|
|
712
|
-
maxSimultaneousBadges: 3,
|
|
713
|
-
maxSimultaneousSpotlights: 1,
|
|
714
|
-
maxToastsPerSession: 3,
|
|
715
|
-
minTimeBetweenModals: 30000,
|
|
716
|
-
minTimeBetweenTours: 86400000,
|
|
717
|
-
sessionCooldown: 5000,
|
|
718
|
-
respectDoNotDisturb: true,
|
|
719
|
-
}}
|
|
720
|
-
>
|
|
721
|
-
<App />
|
|
722
|
-
</FeatureDropProvider>
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
**Add badges to your sidebar:**
|
|
726
|
-
|
|
727
|
-
```tsx
|
|
728
|
-
import { useNewFeature, NewBadge } from 'featuredrop/react'
|
|
729
|
-
|
|
730
|
-
function SidebarItem({ path, label }: { path: string; label: string }) {
|
|
731
|
-
const { isNew, dismiss } = useNewFeature(path)
|
|
732
|
-
return (
|
|
733
|
-
<a href={path} onClick={() => isNew && dismiss()}>
|
|
734
|
-
{label}
|
|
735
|
-
{isNew && <NewBadge />}
|
|
736
|
-
</a>
|
|
737
|
-
)
|
|
738
|
-
}
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
**Or drop in the full changelog widget:**
|
|
742
|
-
|
|
743
|
-
```tsx
|
|
744
|
-
import { ChangelogWidget } from 'featuredrop/react'
|
|
745
|
-
|
|
746
|
-
<ChangelogWidget variant="panel" />
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
**Hooks & Components:**
|
|
750
|
-
|
|
751
|
-
| Export | What it does |
|
|
752
|
-
|--------|-------------|
|
|
753
|
-
| `useFeatureDrop()` | Full context: features, count, dismiss, dismissAll |
|
|
754
|
-
| `useNewFeature(key)` | Single nav item: `{ isNew, feature, dismiss }` |
|
|
755
|
-
| `useNewCount()` | Just the badge count |
|
|
756
|
-
| `useTabNotification()` | Updates browser tab title with count |
|
|
757
|
-
| `useTour(id)` | Imperative tour controls and current step snapshot |
|
|
758
|
-
| `useTourSequencer(sequence)` | Ordered multi-tour orchestration by feature readiness |
|
|
759
|
-
| `useChecklist(id)` | Imperative checklist controls + progress |
|
|
760
|
-
| `useSurvey(id)` | Imperative survey controls (`show`, `hide`, `askLater`) + state |
|
|
761
|
-
| `<NewBadge />` | Headless badge: `pill`, `dot`, or `count` variant |
|
|
762
|
-
| `<ChangelogWidget />` | Full changelog feed with trigger button + optional reactions |
|
|
763
|
-
| `<ChangelogPage />` | Full-page changelog with filters/search/pagination |
|
|
764
|
-
| `<Spotlight />` | Pulsing beacon attached to DOM elements |
|
|
765
|
-
| `<Banner />` | Announcement banner with variants |
|
|
766
|
-
| `<Toast />` | Stackable toast notifications |
|
|
767
|
-
| `<Tour />` | Multi-step guided product tour |
|
|
768
|
-
| `<Checklist />` | Onboarding task checklist |
|
|
769
|
-
| `<FeedbackWidget />` | In-app feedback form with category/emoji/screenshot support |
|
|
770
|
-
| `<Survey />` | NPS/CSAT/CES/custom survey engine with trigger rules |
|
|
771
|
-
| `<FeatureRequestButton />` | Per-feature voting button with persisted vote guard |
|
|
772
|
-
| `<FeatureRequestForm />` | Request capture form + sortable request list |
|
|
773
|
-
| `<Hotspot />` / `<TooltipGroup />` | Contextual tooltips with visibility caps |
|
|
774
|
-
| `<AnnouncementModal />` | Priority/frequency-gated modal announcements |
|
|
775
|
-
| `<SpotlightChain />` | Lightweight chained spotlight walkthrough |
|
|
776
|
-
|
|
777
|
-
`useFeatureDrop()` also exposes queue/throttle controls: `queuedFeatures`, `totalNewCount`, `quietMode`, `setQuietMode`, `markFeatureSeen`, `markFeatureClicked`, toast-slot helpers, modal/tour pacing checks, and spotlight slot controls.
|
|
778
|
-
It also exposes trigger runtime helpers: `trackUsageEvent`, `trackTriggerEvent`, `trackMilestone`, and `setTriggerPath`.
|
|
779
|
-
|
|
780
|
-
**Analytics integration:**
|
|
781
|
-
|
|
782
|
-
```tsx
|
|
783
|
-
<FeatureDropProvider
|
|
784
|
-
manifest={FEATURES}
|
|
785
|
-
storage={storage}
|
|
786
|
-
appVersion="2.5.1" // optional semver for version-pinned features
|
|
787
|
-
analytics={{
|
|
788
|
-
onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
|
|
789
|
-
onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
|
|
790
|
-
onFeatureClicked: (f) => posthog.capture('feature_clicked', { id: f.id }),
|
|
791
|
-
onWidgetOpened: () => posthog.capture('changelog_opened'),
|
|
792
|
-
}}
|
|
793
|
-
>
|
|
794
|
-
```
|
|
795
|
-
|
|
796
|
-
All components accept an optional `analytics` prop for component-level tracking too.
|
|
797
|
-
|
|
798
|
-
## How It Works
|
|
351
|
+
---
|
|
799
352
|
|
|
800
|
-
|
|
801
|
-
Manifest (static) Storage (runtime)
|
|
802
|
-
┌───────────────────┐ ┌──────────────────────┐
|
|
803
|
-
│ releasedAt: Feb 20 │ │ watermark ← server │
|
|
804
|
-
│ showNewUntil: Mar 20│ │ dismissed ← localStorage│
|
|
805
|
-
└────────┬──────────┘ └──────────┬───────────┘
|
|
806
|
-
│ │
|
|
807
|
-
└──────────┐ ┌────────────────┘
|
|
808
|
-
▼ ▼
|
|
809
|
-
┌───────────────┐
|
|
810
|
-
│ isNew() │
|
|
811
|
-
│ │
|
|
812
|
-
│ !dismissed │
|
|
813
|
-
│ !expired │
|
|
814
|
-
│ afterWatermark│
|
|
815
|
-
│ afterPublishAt│
|
|
816
|
-
└───────┬───────┘
|
|
817
|
-
│
|
|
818
|
-
true / false
|
|
819
|
-
```
|
|
353
|
+
## Full Comparison
|
|
820
354
|
|
|
821
|
-
|
|
355
|
+
| | FeatureDrop | Beamer | Headway | AnnounceKit | Pendo |
|
|
356
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
357
|
+
| **Price** | **Free** | $59–399/mo | $49–249/mo | $79–299/mo | $7k+/yr |
|
|
358
|
+
| Auto-expiring badges | ✅ | — | — | — | — |
|
|
359
|
+
| Changelog widget | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
360
|
+
| Product tours | ✅ | — | — | — | ✅ |
|
|
361
|
+
| Onboarding checklists | ✅ | — | — | — | ✅ |
|
|
362
|
+
| Spotlight / beacon | ✅ | — | — | — | — |
|
|
363
|
+
| Hotspot tooltips | ✅ | — | — | — | — |
|
|
364
|
+
| Announcement modal | ✅ | — | — | — | — |
|
|
365
|
+
| Toast notifications | ✅ | — | — | — | — |
|
|
366
|
+
| Feedback & surveys | ✅ | — | — | — | ✅ |
|
|
367
|
+
| Feature request voting | ✅ | — | — | — | — |
|
|
368
|
+
| Tab title notification | ✅ | — | — | — | — |
|
|
369
|
+
| Zero runtime deps (core) | ✅ | — | — | — | — |
|
|
370
|
+
| Framework agnostic | ✅ | — | — | — | — |
|
|
371
|
+
| Headless mode | ✅ | — | — | — | — |
|
|
372
|
+
| Analytics callbacks | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
373
|
+
| Self-hosted | ✅ | — | — | — | — |
|
|
374
|
+
| Open source | ✅ | — | — | — | — |
|
|
822
375
|
|
|
823
|
-
|
|
376
|
+
---
|
|
824
377
|
|
|
825
|
-
|
|
378
|
+
## Documentation
|
|
826
379
|
|
|
827
|
-
|
|
380
|
+
| Resource | Description |
|
|
381
|
+
|---|---|
|
|
382
|
+
| [**Live Docs**](https://featuredrop.dev) | Full documentation site |
|
|
383
|
+
| [Quickstart](https://featuredrop.dev/docs/quickstart) | Ship your first badge in 10 minutes |
|
|
384
|
+
| [Component Gallery](https://featuredrop.dev/docs/components/gallery) | Live interactive demos |
|
|
385
|
+
| [Playground](https://featuredrop.dev/playground) | Local sandbox + hosted templates |
|
|
386
|
+
| [API Reference](https://featuredrop.dev/docs/api) | All functions, hooks, and components |
|
|
387
|
+
| [Migration Guide](https://featuredrop.dev/docs/migration) | Migrate from Beamer, Pendo, Headway |
|
|
388
|
+
| [Architecture](https://featuredrop.dev/docs/concepts/architecture) | Three-check algorithm, cross-device sync |
|
|
389
|
+
| [Recipes](https://featuredrop.dev/docs/recipes) | Copy-paste integration patterns |
|
|
390
|
+
| [Frameworks](https://featuredrop.dev/docs/frameworks/vue) | Vue, Svelte, Solid, Angular, Preact, Web Components |
|
|
828
391
|
|
|
829
|
-
|
|
392
|
+
---
|
|
830
393
|
|
|
831
|
-
##
|
|
394
|
+
## Branding Assets
|
|
832
395
|
|
|
833
|
-
|
|
834
|
-
|---|:---:|:---:|:---:|:---:|:---:|
|
|
835
|
-
| **Price** | **Free** | $59-399/mo | $49-249/mo | $79-299/mo | $79+ |
|
|
836
|
-
| Auto-expiring badges | Yes | - | - | - | - |
|
|
837
|
-
| Changelog widget | Yes | Yes | Yes | Yes | Yes |
|
|
838
|
-
| Product tours | Yes | - | - | - | - |
|
|
839
|
-
| Onboarding checklists | Yes | - | - | - | - |
|
|
840
|
-
| Spotlight/beacon | Yes | - | - | - | - |
|
|
841
|
-
| Hotspot tooltips | Yes | - | - | - | - |
|
|
842
|
-
| Announcement modal | Yes | - | - | - | - |
|
|
843
|
-
| Spotlight chaining | Yes | - | - | - | - |
|
|
844
|
-
| Toast notifications | Yes | - | - | - | - |
|
|
845
|
-
| Announcement banner | Yes | - | - | - | - |
|
|
846
|
-
| Tab title notification | Yes | - | - | - | - |
|
|
847
|
-
| Zero dependencies | Yes | - | - | - | - |
|
|
848
|
-
| Framework agnostic | Yes | - | - | - | - |
|
|
849
|
-
| React bindings | Yes | - | - | - | - |
|
|
850
|
-
| Headless mode | Yes | - | - | - | - |
|
|
851
|
-
| Cross-device sync | Yes | Yes | Yes | Yes | Yes |
|
|
852
|
-
| Per-feature dismiss | Yes | - | - | - | - |
|
|
853
|
-
| Scheduled publishing | Yes | Yes | Yes | Yes | - |
|
|
854
|
-
| Priority levels | Yes | - | - | - | - |
|
|
855
|
-
| Analytics callbacks | Yes | Built-in | Built-in | Built-in | Built-in |
|
|
856
|
-
| < 10 kB minzipped | Yes | - | - | - | - |
|
|
857
|
-
| Self-hosted | Yes | - | - | - | - |
|
|
858
|
-
| Open source | Yes | - | - | - | - |
|
|
859
|
-
|
|
860
|
-
## Framework Support
|
|
396
|
+
All marketing assets are in [`apps/docs/public/og/`](apps/docs/public/og/).
|
|
861
397
|
|
|
862
|
-
|
|
|
863
|
-
|
|
864
|
-
|
|
|
865
|
-
|
|
|
866
|
-
|
|
|
867
|
-
|
|
|
868
|
-
|
|
|
869
|
-
|
|
|
870
|
-
|
|
|
871
|
-
| Svelte 5 | Preview (store bindings) | `featuredrop/svelte` |
|
|
398
|
+
| File | Ratio | Use |
|
|
399
|
+
|---|---|---|
|
|
400
|
+
| `og.png` | 1200×630 (1.91:1) | Website OG / link previews, Discord, Slack |
|
|
401
|
+
| `github-social.png` | 1280×640 (2:1) | **GitHub repo social preview** ← upload this |
|
|
402
|
+
| `twitter-header.png` | 1500×500 (3:1) | X.com profile header |
|
|
403
|
+
| `linkedin-banner.png` | 1584×396 (4:1) | LinkedIn company page banner |
|
|
404
|
+
| `reddit-16x9.png` | 1920×1080 (16:9) | Reddit posts, r/reactjs, r/webdev |
|
|
405
|
+
| `producthunt.png` | 1270×760 | Product Hunt launch |
|
|
406
|
+
| `story-9x16.png` | 1080×1920 (9:16) | Instagram / LinkedIn Stories |
|
|
872
407
|
|
|
873
|
-
|
|
408
|
+
**GitHub social preview**: Repo **Settings → Social preview → Upload** `apps/docs/public/og/github-social.png`.
|
|
874
409
|
|
|
875
|
-
|
|
876
|
-
- [Docs Site Guide](docs/DOCS_SITE.md) — Local commands and Vercel deployment setup
|
|
877
|
-
- [API Reference](docs/API.md) — All functions, adapters, hooks, components
|
|
878
|
-
- [Recipes](docs/RECIPES.md) — Copy-paste integration patterns (sidebar badge, tours, migration, A/B tests)
|
|
879
|
-
- [Architecture](docs/ARCHITECTURE.md) — Three-check algorithm, cross-device sync, custom adapters
|
|
880
|
-
- [Next.js Example](examples/nextjs/) — Full App Router integration
|
|
881
|
-
- [Vanilla Example](examples/vanilla/) — Plain HTML, zero build step
|
|
882
|
-
- [React Sandbox](examples/sandbox-react/) — Interactive local/online playground
|
|
410
|
+
---
|
|
883
411
|
|
|
884
412
|
## Contributing
|
|
885
413
|
|
|
886
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
414
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, commit conventions, and release process.
|
|
887
415
|
|
|
888
416
|
## Security
|
|
889
417
|
|
|
890
|
-
- Report vulnerabilities privately via [SECURITY.md](SECURITY.md)
|
|
891
|
-
- CI includes CodeQL static analysis on
|
|
892
|
-
- `pnpm security-check` scans runtime source for unsafe execution
|
|
418
|
+
- Report vulnerabilities privately via [SECURITY.md](SECURITY.md)
|
|
419
|
+
- CI includes CodeQL static analysis on PRs and `main`
|
|
420
|
+
- `pnpm security-check` scans runtime source for unsafe execution patterns
|
|
893
421
|
|
|
894
422
|
## License
|
|
895
423
|
|
|
896
|
-
MIT
|
|
424
|
+
MIT © [GLINR STUDIOS](https://glincker.com)
|
|
897
425
|
|
|
898
426
|
---
|
|
899
427
|
|
|
900
428
|
<p align="center">
|
|
901
|
-
<sub>Built and battle-tested at <a href="https://askverdict.ai">AskVerdict</a>.</sub
|
|
902
|
-
<
|
|
903
|
-
<strong>A <a href="https://glincker.com">GLINCKER</a> open source project.</strong>
|
|
429
|
+
<sub>Built and battle-tested at <a href="https://askverdict.ai">AskVerdict AI</a>.</sub><br />
|
|
430
|
+
<strong>A <a href="https://glincker.com">GLINR STUDIOS</a> open source project.</strong>
|
|
904
431
|
</p>
|