Migration Guide
FeatureDrop is a code-first replacement for hosted changelog and onboarding tools like Beamer, Pendo, Headway, and Canny. This guide walks through migrating from any vendor.
Field mapping
| Vendor field | FeatureDrop field | Notes |
|---|---|---|
| Title / Headline | label | Plain text |
| Body / Description | description | Supports markdown |
| Category / Tag | category | Single string |
| Publish date | releasedAt | ISO 8601 date string |
| Expiry / Hide after | showNewUntil | ISO 8601 date string |
| CTA button | cta | { label, href } object |
| Type (feature/fix/improvement) | type | "feature" | "improvement" | "fix" |
| Priority | priority | "low" | "medium" | "high" | "critical" |
| Audience / Segment | audience | AudienceRule object |
| Version | version | Semver string |
What migrates cleanly
- Release entries: Title, description, category, dates, CTAs all have direct equivalents
- Categories/tabs: Map to the
categoryfield, used byChangelogWidgetfilter pills - Scheduling windows:
releasedAt+showNewUntilreplace vendor scheduling features - Audience targeting: Vendor segment rules map to
AudienceRulewith plan/role/region fields
What does not migrate automatically
- Read/dismiss state: Vendor-hosted seen/dismiss tracking is proprietary and not exportable. Plan for a one-time state reset (see below).
- Analytics history: Historical impression/click data stays with the vendor. Start fresh with FeatureDrop's analytics callbacks.
- Embedded scripts: Vendor
<script>tags must be removed. FeatureDrop components replace them. - Automations: Vendor-side triggers (email on publish, Slack notifications) need re-implementation via Notification Bridges.
Migration steps
Export old data
Export entries from your current provider. Most vendors support JSON or CSV export:
- Beamer: Settings > Export > JSON
- Pendo: Guides > Export as CSV
- Headway: Account > Export changelog
- Canny: API export or manual copy
Convert to FeatureDrop format
Write a conversion script or manually transform entries:
import { readFileSync, writeFileSync } from 'fs'
// Example: Convert Beamer export to FeatureDrop manifest
const beamerExport = JSON.parse(readFileSync('beamer-export.json', 'utf-8'))
const manifest = beamerExport.map((entry: any) => ({
id: entry.id || entry.slug,
label: entry.title,
description: entry.content,
category: entry.category?.toLowerCase() || 'general',
releasedAt: new Date(entry.date).toISOString(),
showNewUntil: new Date(Date.now() + 30 * 86400000).toISOString(), // 30 days from now
type: mapType(entry.type),
...(entry.link && { cta: { label: 'Learn more', href: entry.link } })
}))
writeFileSync('features.json', JSON.stringify(manifest, null, 2))
function mapType(vendorType: string) {
const typeMap: Record<string, string> = {
new: 'feature',
improvement: 'improvement',
fix: 'fix',
announcement: 'feature'
}
return typeMap[vendorType?.toLowerCase()] || 'feature'
}Validate the manifest
npx featuredrop validate
npx featuredrop doctorFix any schema errors before proceeding.
Handle historical entries
For old entries that should NOT resurface as "new", set showNewUntil to a past date:
{
"id": "old-feature",
"label": "Some old feature",
"releasedAt": "2024-01-15T00:00:00Z",
"showNewUntil": "2024-02-15T00:00:00Z"
}These entries appear in the changelog history but never trigger badges or notifications.
Remove vendor scripts
Remove the vendor's embedded script tag from your HTML:
- <script src="https://app.getbeamer.com/js/beamer-embed.js"></script>
- <script>var beamer_config = { ... }</script>Add FeatureDrop components
Replace vendor widgets with FeatureDrop components:
import { FeatureDropProvider, ChangelogWidget, NewBadge } from 'featuredrop/react'
import { LocalStorageAdapter } from 'featuredrop'
import manifest from './features.json'
<FeatureDropProvider manifest={manifest} storage={new LocalStorageAdapter()}>
<nav>
Settings <NewBadge id="dark-mode" />
</nav>
<ChangelogWidget title="What's new" />
</FeatureDropProvider>Deploy and monitor
Ship the migration. Monitor analytics callbacks to verify impressions and dismissals are firing correctly.
Handling the state reset
Dismissal/read state from SaaS vendors is not portable. All existing users will see migrated features as "new" on first load after migration.
Strategies to minimize disruption:
- Set past
showNewUntilon all historical entries. Only recent entries (last 2-4 weeks) should have future expiry dates. - Use the watermark strategy: Set the initial watermark to a recent date so only truly new entries surface:
const storage = new LocalStorageAdapter({ initialWatermark: '2025-03-01T00:00:00Z' }) - Phased rollout: Deploy FeatureDrop with only new entries first. Add historical entries to the changelog later without triggering badges.
Cost comparison
| Beamer | Pendo | FeatureDrop | |
|---|---|---|---|
| Monthly cost | $49-249/mo | $7,000+/yr | Free |
| Vendor lock-in | Yes | Yes | No |
| Data ownership | Vendor-hosted | Vendor-hosted | Your repo |
| Bundle impact | External script | ~300 kB agent | < 3 kB core |
| Customization | CSS themes | Limited | Full source access |