Skip to main content
userTourKit
@tour-kit/surveysComponents

Turnkey Survey Modals

CsatModal, NpsModal, and CesModal — two-prop wrappers around SurveyModal + QuestionRating with built-in scoring categories.

domidex01Published

CsatModal, NpsModal, and CesModal collapse the common "show a one-question survey" flow into a single import and two required props: question and onSubmit. Each composes the existing SurveyModal + QuestionRating primitives — no new focus-trap, escape-key, or storage code — and ships its own selected-value state plus a Submit/Skip pair.

These components still require an ancestor <SurveysProvider> because the underlying SurveyModal calls useSurvey(surveyId). The wrappers remove primitive imports, not the provider requirement.

Import

import { CsatModal, NpsModal, CesModal } from '@tour-kit/surveys';

CsatModal

A 1–5 rating modal. onSubmit receives the raw rating.

<CsatModal
  question="How easy was checkout?"
  onSubmit={(rating) => track('csat', rating)}
/>

Prop

Type

NpsModal

A 0–10 rating modal. onSubmit receives the score and a derived NpsCategory ('promoter' | 'passive' | 'detractor').

<NpsModal
  question="How likely are you to recommend us?"
  onSubmit={(score, category) => track('nps', { score, category })}
/>

The category is computed by the pure helper computeNpsCategory(score):

ScoreCategory
9 – 10'promoter'
7 – 8'passive'
0 – 6'detractor'

Prop

Type

CesModal

A 1–7 rating modal. onSubmit receives the score and a derived CesCategory ('easy' | 'neutral' | 'difficult').

<CesModal
  question="How easy was that?"
  onSubmit={(score, category) => track('ces', { score, category })}
/>

The category is computed by the pure helper computeCesCategory(score):

ScoreCategory
5 – 7'easy'
4'neutral'
1 – 3'difficult'

Prop

Type

Behaviour shared across all three

  • Internal selected-value state is a transient React.useState<number | null>. Submit is disabled until a rating is picked.
  • The modal closes automatically after Submit or Skip; supply open + onOpenChange for fully controlled state.
  • Reduced motion is inherited from SurveyModal via the motion-safe: Tailwind prefix — no extra setup required.
  • Each modal is tree-shakeable: importing only CsatModal does not pull NpsModal or CesModal.

Props

All three share the same base — they extend SurveyModalProps (excluding surveyId, children, onSubmit, onSelect) and add their own submit signature.

interface CsatModalProps extends Omit<SurveyModalProps, 'surveyId' | 'children' | 'onSubmit' | 'onSelect'> {
  surveyId?: string;        // defaults to React.useId()
  question: string;
  ratingScale?: RatingScale;  // default: 1–5 numeric
  lowLabel?: string;
  highLabel?: string;
  onSubmit: (rating: number) => void;
  onSkip?: () => void;
}

interface NpsModalProps extends Omit<SurveyModalProps, 'surveyId' | 'children' | 'onSubmit' | 'onSelect'> {
  surveyId?: string;
  question: string;
  ratingScale?: RatingScale;  // default: 0–10
  lowLabel?: string;
  highLabel?: string;
  onSubmit: (score: number, category: NpsCategory) => void;
  onSkip?: () => void;
}

interface CesModalProps extends Omit<SurveyModalProps, 'surveyId' | 'children' | 'onSubmit' | 'onSelect'> {
  surveyId?: string;
  question: string;
  ratingScale?: RatingScale;  // default: 1–7
  lowLabel?: string;
  highLabel?: string;
  onSubmit: (score: number, category: CesCategory) => void;
  onSkip?: () => void;
}
  • SurveyModal — the underlying primitive each turnkey wraps.
  • QuestionRating — the rating scale used inside each modal.
  • useSurvey — provider hook called by SurveyModal.
Free & open source

Ship onboarding, not config.

npm i @tour-kit/core is MIT and free. The Pro packages work unlicensed too — a one-time $99 license removes the production watermark when you ship.

MIT-licensed — no signup, no credit card. Pay once, only when you ship.