npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tour-kit/surveys

v3.0.0

Published

React in-app microsurveys — NPS, CSAT, CES with skip logic, audience targeting, fatigue prevention and 5 display modes.

Readme

@tour-kit/surveys

React in-app microsurveys — NPS, CSAT, CES with skip logic, audience targeting, fatigue prevention and 5 display modes.

npm version npm downloads bundle size types

Drop-in in-app microsurveys for React — NPS, CSAT, CES, and custom feedback flows with skip logic, audience targeting, sampling, snooze, and built-in fatigue prevention. Five display modes: Modal, Slideout, Banner, Popover, Inline.

Pro tier — requires a license key. See Licensing.

Alternative to: Delighted, Sprig, Wootric, Typeform embedded surveys, Pendo feedback, Hotjar surveys.

Features

  • NPS, CSAT, CES with built-in scoring helpers (calculateNPS, calculateCSAT, calculateCES)
  • 5 display modes — Modal, Slideout, Banner, Popover, Inline
  • Skip logic — conditional question flow based on previous answers, with cycle detection
  • Fatigue prevention — global cooldown, sampling rate, snooze, max-per-session, frequency rules
  • Audience targeting — show only to matching user segments
  • Question types — rating, text, textarea, single-select, multi-select, boolean
  • Persistence — partial answers survive reload via storage adapter
  • TypeScript-first, supports React 18 & 19

Installation

npm install @tour-kit/surveys @tour-kit/license
# or
pnpm add @tour-kit/surveys @tour-kit/license

Quick Start

import { LicenseProvider } from '@tour-kit/license'
import { SurveysProvider, SurveyModal } from '@tour-kit/surveys'

const npsSurvey = {
  id: 'nps-q4',
  type: 'nps',
  displayMode: 'modal',
  title: 'How likely are you to recommend us?',
  questions: [
    {
      id: 'score',
      type: 'rating',
      scale: { min: 0, max: 10 },
      label: '0 = Not likely, 10 = Extremely likely',
    },
    {
      id: 'reason',
      type: 'textarea',
      label: 'What is the main reason for your score?',
    },
  ],
  frequency: { type: 'interval', days: 90 },
}

function App() {
  return (
    <LicenseProvider licenseKey={process.env.NEXT_PUBLIC_TOURKIT_LICENSE!}>
      <SurveysProvider surveys={[npsSurvey]} userContext={{ plan: 'pro' }}>
        <SurveyModal id="nps-q4" />
        <YourApp />
      </SurveysProvider>
    </LicenseProvider>
  )
}

i18n & interpolation

All user-facing strings in @tour-kit/surveys accept the {{var | fallback}} interpolation grammar from @tour-kit/core. Wrap your tree in <LocaleProvider> and every survey title and question label resolves automatically.

import { LocaleProvider } from '@tour-kit/core'
import { SurveysProvider, SurveyModal } from '@tour-kit/surveys'

const survey = {
  id: 'nps-q4',
  type: 'nps',
  displayMode: 'modal',
  title: { key: 'nps.title' },
  questions: [{ id: 'score', type: 'rating', label: 'Hi {{user.name | there}} — score 0–10', scale: { min: 0, max: 10 } }],
}

<LocaleProvider locale="en" messages={{ 'nps.title': 'How likely is {{user.name | a friend}} to recommend us?' }}>
  <SurveysProvider surveys={[survey]}>
    <SurveyModal id="nps-q4" />
  </SurveysProvider>
</LocaleProvider>

Full guide: https://usertourkit.com/docs/guides/i18n

Survey types

| Type | Scoring | |---|---| | NPS | 0–10 rating → promoters / passives / detractors | | CSAT | rating with positive/negative threshold | | CES | effort score → easy / neutral / difficult | | Custom | arbitrary question flows |

Display modes

| Mode | When to use | |---|---| | Modal | High-priority survey, blocks UI | | Slideout | Detailed survey, non-blocking side panel | | Banner | Lightweight, top/bottom strip | | Popover | Contextual feedback near a target element | | Inline | Embedded in page flow (settings, dashboards) |

Question types

type QuestionType =
  | 'rating'                // numeric scale (NPS 0-10, stars, emoji)
  | 'text' | 'textarea'
  | 'single-select' | 'multi-select'
  | 'boolean'

Fatigue prevention

| Mechanism | Description | |---|---| | Global cooldown | Minimum days between any two surveys (checked first) | | Sampling rate | 0.0–1.0 probability of showing on mount | | Snooze | User-initiated delay; configurable max snooze count | | Max per session | Hard cap on surveys shown in one browser session | | Frequency rules | once, session, always, { type: 'times', count: N }, { type: 'interval', days: N } |

Skip logic

const survey = {
  id: 'csat',
  type: 'csat',
  questions: [
    { id: 'satisfied', type: 'boolean', label: 'Are you satisfied?' },
    {
      id: 'why-not',
      type: 'textarea',
      label: 'What went wrong?',
      skipLogic: { showIf: (answers) => answers.satisfied === false },
    },
  ],
}

The FlowEngine tracks visited steps to prevent infinite loops in skip chains.

API Reference

Provider & context

import { SurveysProvider, useSurveysContext } from '@tour-kit/surveys'

Hooks

| Hook | Description | |---|---| | useSurvey(id) | Single survey state + show, dismiss, complete, answer, next, prev, snooze | | useSurveys() | All registered surveys + queue inspection | | useSurveyScoring() | Score aggregation across responses |

Scoring functions

import { calculateNPS, calculateCSAT, calculateCES } from '@tour-kit/surveys'

const result = calculateNPS(responses)
// { score: 42, promoters: 60, passives: 22, detractors: 18 }

Display components

| Export | Mode | |---|---| | SurveyModal | Centered dialog | | SurveySlideout | Side panel | | SurveyBanner | Top/bottom strip | | SurveyPopover | Floating near a target | | SurveyInline | Embedded in page flow |

Question components

QuestionRating, QuestionText, QuestionSelect, QuestionBoolean, SurveyProgress — each with their own *Props type.

Variants (CVA)

import {
  ratingOptionVariants,
  textInputVariants,
  selectOptionVariants,
  booleanOptionVariants,
  progressBarVariants,
} from '@tour-kit/surveys'

Types

import type {
  SurveyConfig,
  SurveyState,
  SurveyType,                  // 'nps' | 'csat' | 'ces' | 'custom'
  DisplayMode,                 // 'modal' | 'slideout' | 'banner' | 'popover' | 'inline'
  SurveyPriority,
  FrequencyRule,
  DismissalReason,
  SurveyStorageAdapter,
  AudienceCondition,
  // Question
  QuestionConfig, QuestionType, AnswerValue, SkipLogic, RatingScale, SelectOption,
  // Scoring
  NPSResult, CSATResult, CESResult,
  // Variant options
  ModalOptions, SlideoutOptions, BannerOptions, PopoverOptions,
  // Position
  SlideoutPosition, BannerPosition, PopoverPosition,
  // Queue
  SurveyQueueConfig, SurveyQueueItem, PriorityOrder, StackBehavior,
  // Events
  SurveyEvent, SurveyEventType,
  // Context
  SurveysContextValue, SurveysProviderProps,
} from '@tour-kit/surveys'

Gotchas

  • Cooldown short-circuits. Global cooldown is checked first — if within cooldown, no survey shows regardless of individual frequency rules.
  • Sampling on mount. The random sampling check runs once when SurveysProvider mounts. To re-roll, remount the provider.
  • Partial responses persist immediately. Each answer() call writes to storage; you don't lose data on reload.
  • Audience targeting needs userContext. Pass it to the provider, otherwise audience-targeted surveys never show.

Related packages

Documentation

Full documentation: https://usertourkit.com/docs/surveys

License

Pro tier — see LICENSE.md. Requires a Tour Kit Pro license key.