Skip to main content
userTourKit
@tour-kit/surveysProviders

SurveysProvider

Root provider that manages survey state, fatigue prevention, persistence, and analytics callbacks for all surveys in your application

domidex01Published Updated

The root provider for @tour-kit/surveys. Place it once, high in your component tree. All display components and hooks must be descendants of this provider.

Import

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

Usage

import { SurveysProvider } from '@tour-kit/surveys';
import type { SurveyConfig } from '@tour-kit/surveys';

const surveys: SurveyConfig[] = [
  {
    id: 'nps-q1',
    type: 'nps',
    displayMode: 'modal',
    title: 'How likely are you to recommend us?',
    questions: [
      {
        id: 'score',
        type: 'rating',
        text: 'Rate us 0–10',
        ratingScale: { min: 0, max: 10 },
      },
    ],
  },
];

export function App() {
  return (
    <SurveysProvider
      surveys={surveys}
      globalCooldownDays={14}
      maxPerSession={1}
      onSurveyComplete={(id, responses) => {
        analytics.track('survey_completed', { id, responses: Object.fromEntries(responses) });
      }}
    >
      <YourApp />
    </SurveysProvider>
  );
}

Props

Prop

Type

Persistence

The provider persists full survey state to storage on every state change and hydrates on mount. The stored JSON includes isCompleted, isDismissed, isSnoozed, snoozeUntil, viewCount, lastViewedAt, responses, and the current queue.

Disable persistence entirely:

<SurveysProvider surveys={surveys} storage={null}>
  <App />
</SurveysProvider>

Use a custom adapter (for example, a server-side or encrypted store):

const adapter: Storage = {
  getItem: (key) => myDb.get(key),
  setItem: (key, value) => myDb.set(key, value),
  removeItem: (key) => myDb.delete(key),
};

<SurveysProvider surveys={surveys} storage={adapter}>
  <App />
</SurveysProvider>

Global cooldown is the first gate evaluated. If the provider's globalCooldownDays has not elapsed since the last shown survey, no individual survey's frequency rule is evaluated and show() is a no-op. If you are testing frequency behaviour, set globalCooldownDays={0} or clear localStorage between runs.

Tour suppression

The provider reads from @tour-kit/core's TourContext. If a guided tour is active, surveys are suppressed automatically — show() becomes a no-op and any currently visible survey is hidden until the tour ends.

Queue configuration

By default, only one survey shows at a time. Requesting a second survey while one is active adds it to an internal priority queue.

<SurveysProvider
  surveys={surveys}
  queueConfig={{
    maxConcurrent: 1,
    priorityOrder: 'priority',   // 'priority' | 'fifo' | 'lifo'
    stackBehavior: 'queue',      // 'queue' | 'replace' | 'stack'
    delayBetween: 500,           // ms between consecutive surveys
    autoShow: true,              // drain queue automatically
  }}
>
  <App />
</SurveysProvider>

useSurveysContext

Low-level context hook that returns the full SurveysContextValue. Prefer useSurvey and useSurveys for most use-cases.

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

function AdminDebugPanel() {
  const ctx = useSurveysContext();

  return (
    <ul>
      {Array.from(ctx.surveys.entries()).map(([id, state]) => (
        <li key={id}>
          {id}: visible={String(state.isVisible)}, completed={String(state.isCompleted)}
        </li>
      ))}
    </ul>
  );
}

Throws if called outside a SurveysProvider.