Docs
Migration

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 fieldFeatureDrop fieldNotes
Title / HeadlinelabelPlain text
Body / DescriptiondescriptionSupports markdown
Category / TagcategorySingle string
Publish datereleasedAtISO 8601 date string
Expiry / Hide aftershowNewUntilISO 8601 date string
CTA buttoncta{ label, href } object
Type (feature/fix/improvement)type"feature" | "improvement" | "fix"
Prioritypriority"low" | "medium" | "high" | "critical"
Audience / SegmentaudienceAudienceRule object
VersionversionSemver string

What migrates cleanly

  • Release entries: Title, description, category, dates, CTAs all have direct equivalents
  • Categories/tabs: Map to the category field, used by ChangelogWidget filter pills
  • Scheduling windows: releasedAt + showNewUntil replace vendor scheduling features
  • Audience targeting: Vendor segment rules map to AudienceRule with 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:

scripts/migrate.ts
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 doctor

Fix 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:

  1. Set past showNewUntil on all historical entries. Only recent entries (last 2-4 weeks) should have future expiry dates.
  2. 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' })
  3. Phased rollout: Deploy FeatureDrop with only new entries first. Add historical entries to the changelog later without triggering badges.

Cost comparison

BeamerPendoFeatureDrop
Monthly cost$49-249/mo$7,000+/yrFree
Vendor lock-inYesYesNo
Data ownershipVendor-hostedVendor-hostedYour repo
Bundle impactExternal script~300 kB agent< 3 kB core
CustomizationCSS themesLimitedFull source access