Feature Guide
Everything SparkGuide can do, with a working example for each. If you haven't installed it yet, start with Getting Started.
πΆ Product tours (flows)
A flow is an ordered sequence of steps β the thing most
people mean by "product tour." Register one with addFlow,
then start it.
guide.addFlow('reports-tour', {
steps: [
{ type: 'modal', title: 'New: Reports', content: 'Hereβs what changed.' },
{ target: '#reports-table', content: 'Your saved reports now live here.' }
]
});
guide.start('reports-tour'); // start (or resume) it
guide.start('reports-tour', { resume: false }); // always replay from step 0
guide.startMatching(); // start every flow whose
// segment matches β great
// for a page-load hook
guide.dismiss('reports-tour'); // close it early
guide.resetFlow('reports-tour'); // clear its saved progress
startMatching() is the method most apps call once on page
load: it walks every registered flow, checks its segment
rule against the current user, and shows the first one that matches and
hasn't already been seen.
startMatching() runs them one at a time β
each is awaited until it's completed or dismissed before the next one
starts, so you never get two tours' overlays stacked on top of each other.
π±οΈ Step types: tooltip, hotspot, modal
Each step in a flow renders one of three components. The type is
inferred from the step's shape unless you set type explicitly:
| Step looks like | Renders as |
|---|---|
has a target, no type | Tooltip β spotlight overlay + positioned card |
type: 'hotspot' | Hotspot β a pulsing dot; click it to reveal the content |
type: 'modal', or no target | Modal β centered dialog, not anchored to anything |
{
type: 'tooltip' | 'hotspot' | 'modal', // default: tooltip if `target` set, else modal
target: '#css-selector', // omit for modal steps
title: 'string',
content: 'string',
placement: 'top' | 'bottom' | 'left' | 'right', // default 'bottom'
advanceOn: 'click', // optional: auto-advance when the target is clicked
showOverlay: true // tooltip-only: set false to skip the dimmed backdrop
}
Tooltips
Dim the rest of the page and cut out a "spotlight" around the target, with Back/Next (or Finish, on the last step) buttons.
Hotspots
Non-blocking β no dimmed backdrop, just a small pulsing indicator. Clicking it opens a popover with the content and, inside a flow, the same Back/Next navigation as a tooltip.
Modals
Centered dialogs, ideal for a flow's opening "Welcome" screen since they don't need to anchor to any element on the page.
πͺ Standalone modals
Not every dialog needs to be part of a multi-step flow:
guide.showModal({
title: 'Heads up',
content: 'Hereβs something worth knowing.',
primaryLabel: 'Got it',
secondaryLabel: 'Remind me later',
onPrimary: () => trackEvent('notice_ack'),
onSecondary: () => scheduleReminder()
});
onPrimary/onSecondary/onClose are
all optional β every control on the modal (the X, clicking outside, and
both buttons) closes it regardless of whether you pass a callback.
π Standalone hotspots
For a lightweight "psst, try this" nudge outside of any flow:
guide.showHotspot({
target: '#new-feature-icon', // a selector (waits for it to appear) or an Element
title: 'New: Dark mode',
content: 'Toggle it from here.',
placement: 'right'
});
π£ Banners
A persistent top-of-page bar for announcements:
guide.showBanner({
content: 'New: Roadmap & feature requests are here.',
actionLabel: 'See what shipped',
onAction: () => (window.location.href = '/changelog'),
onClose: () => trackEvent('banner_dismissed')
});
Calling showBanner again replaces the current banner with a new one.
β Checklists
A floating "getting started" checklist with a progress bar. Items can be checked off by the user, or completed programmatically from your own app code in response to a real action:
guide.addChecklist('getting-started', {
title: 'Getting started',
items: [
{ id: 'invite', label: 'Invite your team' },
{ id: 'project', label: 'Create your first project' },
{ id: 'integration', label: 'Connect an integration' }
]
});
// later, wherever the real action happens in your app:
createProjectButton.addEventListener('click', () => {
guide.completeChecklistItem('getting-started', 'project');
});
The checklist minimizes to a small floating button and remembers which
items are done across reloads. checklist:complete fires
once, the moment the last item is checked β not again if an item is
later unchecked and rechecked.
π― Segmentation & targeting
Any flow's segment option decides who sees it. All rules given are AND'd together:
guide.addFlow('admin-only-tour', {
segment: {
urlMatches: 'contains:/dashboard', // or 'equals:/app', or a RegExp
elementExists: '#admin-panel', // only if this element is on the page
user: { role: 'admin', plan: ['pro', 'enterprise'] } // value or array of allowed values
},
steps: [ /* ... */ ]
});
Tell SparkGuide who the current user is with identify()
(merges into whatever you passed at construction time), and re-check
segments any time:
guide.identify({ plan: 'pro' }); // e.g. right after an upgrade
guide.startMatching(); // re-evaluate and show anything newly unlocked
You can also call the matcher directly β handy for building your own "would this flow show right now?" debug panel:
import { Targeting } from '@capsbharg/sparkguide-js';
Targeting.matches(
{ user: { plan: 'free' }, elementExists: '#upgrade-banner' },
{ user: { plan: 'free', role: 'member' } }
); // => true
π‘ Events
Subscribe with guide.on(event, handler) (returns an unsubscribe function).
| Event | Fires when |
|---|---|
flow:start | a flow begins |
flow:complete | a flow finishes its last step |
flow:dismiss | a flow is closed early |
step:show | a step renders β (flowId, stepIndex, step) |
step:complete | a step is passed β (flowId, stepIndex, step) |
checklist:item | a checklist item is toggled β (checklistId, itemId, done) |
checklist:complete | every item in a checklist is done |
guide.on('step:show', (flowId, index, step) => {
analytics.track('onboarding_step_viewed', { flowId, index });
});
π¨ Theming
Pass a theme object at construction β everything is
exposed as a CSS custom property under the hood, so no separate
stylesheet is ever needed:
new SparkGuide({
theme: {
primaryColor: '#4f46e5', // buttons, progress bars, the hotspot dot
textColor: '#1f2933',
backgroundColor: '#ffffff',
overlayColor: 'rgba(15, 23, 42, 0.55)',
borderRadius: '10px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
zIndex: 999999
}
});
Creating a second SparkGuide instance with a different
theme (e.g. after a user switches workspaces) updates the shared
stylesheet in place β the newest theme always wins.
πΎ Persistence & replaying tours
Progress lives in localStorage, namespaced by
storageKey, so it's safe to run multiple independent
guides (e.g. one per logged-in account) side by side on the same origin.
-
resume: true(the default, used bystart()andstartMatching()) skips a flow that's already been completed or dismissed β closing a tour early means "don't show me this again," not "ask me later." -
resume: falseis an explicit "run this now" β it always replays the flow from step 0, regardless of history. Use it for a "Replay tour" button in a help menu. -
resetFlow(id)clears a flow's saved state entirely, as if the user had never seen it β useful for a "reset my onboarding" debug action.