Docs
Components
Launch Flow

Composite Launch Flows

FeatureDrop is strongest when components are orchestrated as a sequence, not used in isolation. A single feature launch can span banner, tour, checklist, and feedback in a coordinated flow.

Typical launch sequence

StageComponentGoalInterruption
Awareness<Banner>Announce change existsLow
Guidance<Tour> / <SpotlightChain>Teach the workflowMedium
Activation<Checklist>Drive completionMedium
Follow-up<FeedbackWidget> / <Survey>Measure qualityLow

Coordination pattern

Use callbacks to chain components explicitly. You control sequencing logic in app code instead of hidden SaaS automation rules.

LaunchFlow.tsx
import { useState } from 'react'
import { Banner, Tour, Checklist, FeedbackWidget } from 'featuredrop/react'
 
const tourSteps = [
  { id: 'step-1', target: '#data-source', title: 'Connect', content: 'Link your database here.' },
  { id: 'step-2', target: '#sync-btn', title: 'Sync', content: 'Click to start syncing.' },
]
 
const tasks = [
  { id: 'connect', title: 'Connect data source', estimatedTime: '2m' },
  { id: 'sync', title: 'Run first sync', estimatedTime: '1m' },
  { id: 'verify', title: 'Verify data', estimatedTime: '3m' },
]
 
export function LaunchFlow() {
  const [phase, setPhase] = useState<'banner' | 'tour' | 'checklist' | 'feedback'>('banner')
 
  return (
    <>
      {/* Phase 1: Banner announces the feature */}
      {phase === 'banner' && (
        <Banner
          featureId="data-sync"
          position="inline"
          onDismiss={() => setPhase('tour')}
        />
      )}
 
      {/* Phase 2: Tour teaches the workflow */}
      {phase === 'tour' && (
        <Tour
          id="data-sync-tour"
          steps={tourSteps}
          onComplete={() => setPhase('checklist')}
          onSkip={() => setPhase('checklist')}
        >
          {({ isActive, step, startTour, nextStep, skipTour }) => (
            <div>
              {!isActive && <button onClick={startTour}>Start tour</button>}
              {isActive && step && (
                <div>
                  <h4>{step.title}</h4>
                  <p>{String(step.content)}</p>
                  <button onClick={nextStep}>Next</button>
                  <button onClick={skipTour}>Skip</button>
                </div>
              )}
            </div>
          )}
        </Tour>
      )}
 
      {/* Phase 3: Checklist drives activation */}
      {phase === 'checklist' && (
        <Checklist
          id="data-sync-checklist"
          tasks={tasks}
          position="inline"
          onComplete={() => setPhase('feedback')}
        />
      )}
 
      {/* Phase 4: Feedback collects signal */}
      {phase === 'feedback' && (
        <FeedbackWidget
          featureId="data-sync"
          onSubmit={async (payload) => {
            console.log('Feedback:', payload)
          }}
        >
          {({ isOpen, open, text, setText, submit }) => (
            <div>
              {!isOpen ? (
                <button onClick={open}>How was the setup?</button>
              ) : (
                <div>
                  <textarea value={text} onChange={(e) => setText(e.target.value)} />
                  <button onClick={submit}>Send feedback</button>
                </div>
              )}
            </div>
          )}
        </FeedbackWidget>
      )}
    </>
  )
}

Throttling

FeatureDrop's built-in throttler prevents multiple high-interruption surfaces from firing simultaneously. When a tour is active, new spotlights and modals queue until it completes.

<FeatureDropProvider
  manifest={manifest}
  storage={storage}
  throttle={{ maxConcurrent: 1, cooldownMs: 5000 }}
>
  {/* Only one tour/spotlight/modal visible at a time */}
  <App />
</FeatureDropProvider>

Analytics across the flow

Track the entire funnel by wiring analytics callbacks:

<FeatureDropProvider
  manifest={manifest}
  storage={storage}
  analytics={{
    onFeatureImpression: (feature, surface) => {
      track('feature_impression', { id: feature.id, surface })
    },
    onFeatureDismissed: (feature) => {
      track('feature_dismissed', { id: feature.id })
    }
  }}
>
  <LaunchFlow />
</FeatureDropProvider>

Coordination is explicit by design. You control sequencing logic in app code instead of hidden SaaS automation rules. This makes flows testable, debuggable, and version-controlled.

Interactive Demo

When to use launch flows

  • Major feature launches that need guided onboarding
  • Pricing tier upgrades where new capabilities should be highlighted
  • Migration workflows that require multiple steps
  • Quarterly release roundups combining changelog + tours

For routine feature updates, a simple <NewBadge> or changelog entry is sufficient. Reserve multi-surface flows for launches where adoption directly impacts business metrics.