# Surveys

Surveys allow you to collect user feedback, run NPS surveys, and gather qualitative data directly within your application.

## Capabilities

### Get Surveys

Gets all available surveys for the current user.

```typescript { .api }
/**
 * Gets all available surveys
 * @param callback - Function to call with surveys
 * @param forceReload - Force reload from server (default: false)
 */
function getSurveys(callback: SurveyCallback, forceReload?: boolean): void;
```

**Usage Examples:**

```typescript
// Get all surveys
posthog.getSurveys((surveys, context) => {
    console.log('Available surveys:', surveys);

    if (context?.error) {
        console.error('Error loading surveys:', context.error);
    }

    if (context?.isLoaded) {
        displaySurveyList(surveys);
    }
});

// Force reload from server
posthog.getSurveys((surveys) => {
    updateSurveyUI(surveys);
}, true);
```

### Get Active Matching Surveys

Gets surveys that match the current conditions (URL, selector, events, etc.).

```typescript { .api }
/**
 * Gets surveys that match current conditions
 * @param callback - Function to call with matching surveys
 * @param forceReload - Force reload from server (default: false)
 */
function getActiveMatchingSurveys(
    callback: SurveyCallback,
    forceReload?: boolean
): void;
```

**Usage Examples:**

```typescript
// Get surveys for current page
posthog.getActiveMatchingSurveys((surveys) => {
    if (surveys.length > 0) {
        console.log('Surveys available on this page:', surveys);
        showSurveyPrompt(surveys[0]);
    }
});

// Force reload and check
posthog.getActiveMatchingSurveys((surveys) => {
    surveys.forEach(survey => {
        console.log(`Survey "${survey.name}" is active`);
    });
}, true);
```

### Display Survey

Displays a survey with custom display options (popover or inline).

```typescript { .api }
/**
 * Displays a survey with custom options
 * @param surveyId - Survey ID to display
 * @param options - Display options (popover or inline)
 */
function displaySurvey(surveyId: string, options?: DisplaySurveyOptions): void;
```

**Usage Examples:**

```typescript
// Display as popover (default)
posthog.displaySurvey('survey-123');

// Display with popover options
posthog.displaySurvey('nps-survey', {
    type: DisplaySurveyType.Popover,
    position: SurveyPosition.BottomRight,
    backgroundColor: '#ffffff',
    submitButtonText: 'Send Feedback'
});

// Display inline at specific element
posthog.displaySurvey('feedback-survey', {
    type: DisplaySurveyType.Inline,
    selector: '#survey-container'
});
```

### Render Survey

Renders a survey at a specific DOM selector (inline display).

```typescript { .api }
/**
 * Renders a survey at the specified DOM selector
 * @param surveyId - Survey ID to render
 * @param selector - CSS selector for container element
 */
function renderSurvey(surveyId: string, selector: string): void;
```

**Usage Examples:**

```typescript
// Render in specific container
posthog.renderSurvey('survey-123', '#feedback-container');

// Render in modal
posthog.renderSurvey('nps-survey', '.modal-body');

// Render after element exists
function showSurvey(surveyId) {
    const container = document.querySelector('#survey-area');
    if (container) {
        posthog.renderSurvey(surveyId, '#survey-area');
    }
}
```

### Cancel Pending Survey

Cancels a survey that's scheduled to display after a delay.

```typescript { .api }
/**
 * Cancels a pending time-delayed survey
 * @param surveyId - Survey ID to cancel
 */
function cancelPendingSurvey(surveyId: string): void;
```

**Usage Examples:**

```typescript
// Cancel delayed survey
posthog.cancelPendingSurvey('welcome-survey');

// Cancel on route change
router.on('routeChange', () => {
    posthog.cancelPendingSurvey('page-specific-survey');
});

// Cancel on user action
function dismissAllSurveys() {
    posthog.cancelPendingSurvey('survey-1');
    posthog.cancelPendingSurvey('survey-2');
}
```

### Check If Survey Can Render

Asynchronously checks if a survey can be rendered and returns the reason.

```typescript { .api }
/**
 * Checks if a survey can be rendered and returns the reason
 * @param surveyId - Survey ID to check
 * @param forceReload - Force reload surveys from server (default: false)
 * @returns Promise with render status and reason
 */
function canRenderSurveyAsync(
    surveyId: string,
    forceReload?: boolean
): Promise<SurveyRenderReason>;
```

**Usage Examples:**

```typescript
// Check if survey can be shown
const result = await posthog.canRenderSurveyAsync('survey-123');
if (result.canRender) {
    posthog.displaySurvey('survey-123');
} else {
    console.log('Cannot show survey:', result.reason);
}

// Force reload before checking
const result = await posthog.canRenderSurveyAsync('survey-123', true);

// Handle different reasons
async function smartSurveyDisplay(surveyId) {
    const { canRender, reason } = await posthog.canRenderSurveyAsync(surveyId);

    if (canRender) {
        posthog.displaySurvey(surveyId);
    } else {
        console.log(`Survey not shown: ${reason}`);
        // Reasons could be: already completed, rate limited, conditions not met, etc.
    }
}
```

### Survey Load Callback

Registers a callback that fires when surveys are loaded.

```typescript { .api }
/**
 * Registers a callback for when surveys are loaded
 * @param callback - Function to call when surveys are loaded
 * @returns Function to unsubscribe from callback
 */
function onSurveysLoaded(callback: SurveyCallback): () => void;
```

**Usage Examples:**

```typescript
// Listen for survey loading
const unsubscribe = posthog.onSurveysLoaded((surveys, context) => {
    console.log('Surveys loaded:', surveys.length);

    if (context?.isLoaded) {
        initializeSurveyUI(surveys);
    }

    if (context?.error) {
        handleSurveyError(context.error);
    }
});

// Stop listening
unsubscribe();

// One-time initialization
posthog.onSurveysLoaded((surveys) => {
    if (surveys.length > 0) {
        setupSurveyTriggers(surveys);
    }
})();
```

## Types

### SurveyCallback

```typescript { .api }
/**
 * Callback invoked when surveys are loaded
 */
type SurveyCallback = (
    surveys: Survey[],
    context?: { isLoaded: boolean; error?: string }
) => void;
```

### Survey

```typescript { .api }
/**
 * Survey object containing all survey data
 */
interface Survey {
    /**
     * Unique survey identifier
     */
    id: string;

    /**
     * Survey name
     */
    name: string;

    /**
     * Survey description
     */
    description: string;

    /**
     * Survey type
     */
    type: SurveyType;

    /**
     * Survey questions
     */
    questions: SurveyQuestion[];

    /**
     * Visual appearance settings
     */
    appearance: SurveyAppearance | null;

    /**
     * Conditions for displaying the survey
     */
    conditions: {
        /**
         * URL pattern to match
         */
        url?: string;

        /**
         * CSS selector to match
         */
        selector?: string;

        /**
         * Events that trigger the survey
         */
        events?: { values: SurveyEventWithFilters[] } | null;

        /**
         * Wait time before showing (seconds)
         */
        wait_period_days?: number;

        /**
         * Response rate limit
         */
        response_rate_limit?: number;
    } | null;

    /**
     * When survey starts being active
     */
    start_date?: string;

    /**
     * When survey stops being active
     */
    end_date?: string;

    /**
     * Survey targeting configuration
     */
    targeting?: {
        flag_key?: string;
        variant?: string;
    };
}
```

### SurveyType

```typescript { .api }
/**
 * Type of survey display
 */
enum SurveyType {
    /**
     * Popover overlay survey
     */
    Popover = 'popover',

    /**
     * API-only survey (no UI)
     */
    API = 'api',

    /**
     * Widget survey (e.g., feedback button)
     */
    Widget = 'widget',

    /**
     * External survey (link to external tool)
     */
    ExternalSurvey = 'external_survey',
}
```

### SurveyQuestion

```typescript { .api }
/**
 * Union type for all survey question types
 */
type SurveyQuestion =
    | BasicSurveyQuestion
    | LinkSurveyQuestion
    | RatingSurveyQuestion
    | MultipleSurveyQuestion;

/**
 * Basic text question
 */
interface BasicSurveyQuestion {
    type: SurveyQuestionType.Open;
    question: string;
    description?: string;
    required?: boolean;
    placeholder?: string;
}

/**
 * Link/button question
 */
interface LinkSurveyQuestion {
    type: SurveyQuestionType.Link;
    question: string;
    link: string;
    buttonText?: string;
}

/**
 * Rating question (numerical scale)
 */
interface RatingSurveyQuestion {
    type: SurveyQuestionType.Rating;
    question: string;
    description?: string;
    required?: boolean;
    scale: number;
    lowerBoundLabel?: string;
    upperBoundLabel?: string;
}

/**
 * Multiple choice question
 */
interface MultipleSurveyQuestion {
    type: SurveyQuestionType.MultipleChoice | SurveyQuestionType.SingleChoice;
    question: string;
    description?: string;
    required?: boolean;
    choices: string[];
    hasOpenChoice?: boolean;
}
```

### SurveyQuestionType

```typescript { .api }
/**
 * Types of survey questions
 */
enum SurveyQuestionType {
    /**
     * Open text response
     */
    Open = 'open',

    /**
     * Multiple choice (multiple selections)
     */
    MultipleChoice = 'multiple_choice',

    /**
     * Single choice (one selection)
     */
    SingleChoice = 'single_choice',

    /**
     * Rating scale
     */
    Rating = 'rating',

    /**
     * Link/button
     */
    Link = 'link',
}
```

### SurveyAppearance

```typescript { .api }
/**
 * Visual appearance settings for surveys
 */
interface SurveyAppearance {
    /**
     * Background color
     */
    backgroundColor?: string;

    /**
     * Text color
     */
    textColor?: string;

    /**
     * Submit button text
     */
    submitButtonText?: string;

    /**
     * Submit button color
     */
    submitButtonColor?: string;

    /**
     * Rating button color
     */
    ratingButtonColor?: string;

    /**
     * Border color
     */
    borderColor?: string;

    /**
     * Position on screen
     */
    position?: SurveyPosition;

    /**
     * Placeholder text color
     */
    placeholderTextColor?: string;

    /**
     * Thank you message after submission
     */
    thankYouMessage?: string;

    /**
     * Display thank you message inline or as redirect
     */
    thankYouMessageDisplay?: 'inline' | 'redirect';

    /**
     * URL to redirect to after submission (if redirect)
     */
    thankYouMessageRedirect?: string;

    /**
     * Auto-dismiss after submission (milliseconds)
     */
    autoDismiss?: number;

    /**
     * Width of survey (pixels or percentage)
     */
    width?: string | number;

    /**
     * Max width of survey
     */
    maxWidth?: string | number;

    /**
     * Padding
     */
    padding?: string | number;

    /**
     * Border radius
     */
    borderRadius?: string | number;

    /**
     * Show PostHog branding
     */
    showBranding?: boolean;
}
```

### SurveyPosition

```typescript { .api }
/**
 * Position for popover surveys
 */
enum SurveyPosition {
    TopLeft = 'top_left',
    TopRight = 'top_right',
    TopCenter = 'top_center',
    MiddleLeft = 'middle_left',
    MiddleRight = 'middle_right',
    MiddleCenter = 'middle_center',
    BottomLeft = 'bottom_left',
    BottomRight = 'bottom_right',
    BottomCenter = 'bottom_center',
    Left = 'left',
    Center = 'center',
    Right = 'right',
    NextToTrigger = 'next_to_trigger',
}
```

### DisplaySurveyOptions

```typescript { .api }
/**
 * Options for displaying a survey
 */
type DisplaySurveyOptions = DisplaySurveyPopoverOptions | DisplaySurveyInlineOptions;

/**
 * Options for popover display
 */
interface DisplaySurveyPopoverOptions {
    type: DisplaySurveyType.Popover;
    position?: SurveyPosition;
    backgroundColor?: string;
    textColor?: string;
    submitButtonText?: string;
    submitButtonColor?: string;
    ratingButtonColor?: string;
    borderColor?: string;
    placeholder?: string;
}

/**
 * Options for inline display
 */
interface DisplaySurveyInlineOptions {
    type: DisplaySurveyType.Inline;
    selector: string;
}
```

### DisplaySurveyType

```typescript { .api }
/**
 * Survey display mode
 */
enum DisplaySurveyType {
    /**
     * Show as popover overlay
     */
    Popover = 'popover',

    /**
     * Show inline in page
     */
    Inline = 'inline',
}
```

### SurveyRenderReason

```typescript { .api }
/**
 * Result of checking if survey can be rendered
 */
interface SurveyRenderReason {
    /**
     * Whether the survey can be rendered
     */
    canRender: boolean;

    /**
     * Reason why it can or cannot be rendered
     */
    reason: string;
}
```

### SurveyEventName

```typescript { .api }
/**
 * Built-in survey event names
 */
enum SurveyEventName {
    SHOWN = 'survey shown',
    DISMISSED = 'survey dismissed',
    SENT = 'survey sent',
    ABANDONED = 'survey abandoned',
}
```

## Configuration

### Disable Surveys

Configure survey behavior during initialization:

```typescript { .api }
// Disable surveys entirely
posthog.init('token', {
    disable_surveys: true
});

// Disable automatic survey display (manual control only)
posthog.init('token', {
    disable_surveys_automatic_display: true
});
```

## Best Practices

### Wait for Surveys to Load

Always wait for surveys to be loaded before displaying:

```typescript
// ✅ Good: Wait for surveys
posthog.onSurveysLoaded((surveys) => {
    if (surveys.length > 0) {
        displayFirstSurvey(surveys[0]);
    }
});

// ✅ Good: Check before displaying
posthog.getActiveMatchingSurveys((surveys) => {
    if (surveys.length > 0) {
        posthog.displaySurvey(surveys[0].id);
    }
});

// ❌ Bad: Display immediately (may not be loaded)
posthog.displaySurvey('survey-123'); // May fail if surveys not loaded
```

### Verify Survey Can Render

Check if survey can be shown before displaying:

```typescript
// ✅ Good: Verify before displaying
async function showSurveyIfPossible(surveyId) {
    const { canRender, reason } = await posthog.canRenderSurveyAsync(surveyId);

    if (canRender) {
        posthog.displaySurvey(surveyId);
    } else {
        console.log(`Survey not shown: ${reason}`);
        // Handle gracefully
    }
}

// ❌ Bad: Display without checking
posthog.displaySurvey('survey-123');
// May be rate limited, already completed, or conditions not met
```

### Handle Survey Lifecycle

Track survey events for analytics:

```typescript
// Listen for survey events
posthog.on('eventCaptured', (data) => {
    switch (data.event) {
        case 'survey shown':
            console.log('Survey displayed:', data.properties.survey_id);
            break;
        case 'survey sent':
            console.log('Survey completed:', data.properties.survey_id);
            trackSurveyCompletion(data.properties);
            break;
        case 'survey dismissed':
            console.log('Survey dismissed:', data.properties.survey_id);
            break;
    }
});
```

### Custom Survey Timing

Control when surveys are displayed:

```typescript
// Show survey after user completes action
async function onPurchaseComplete() {
    await savePurchase();

    // Show NPS survey
    posthog.getActiveMatchingSurveys((surveys) => {
        const npsSurvey = surveys.find(s => s.name.includes('NPS'));
        if (npsSurvey) {
            posthog.displaySurvey(npsSurvey.id);
        }
    });
}

// Delayed survey display
async function showDelayedSurvey(surveyId, delayMs) {
    await new Promise(resolve => setTimeout(resolve, delayMs));

    const { canRender } = await posthog.canRenderSurveyAsync(surveyId);
    if (canRender) {
        posthog.displaySurvey(surveyId);
    }
}

// Show survey after time on page
let timeOnPage = 0;
setInterval(() => {
    timeOnPage += 1000;
    if (timeOnPage === 30000) { // 30 seconds
        showFeedbackSurvey();
    }
}, 1000);
```

### Inline Survey Integration

Integrate surveys into your UI:

```typescript
// React component example
function FeedbackSection() {
    const surveyRef = useRef(null);

    useEffect(() => {
        posthog.onSurveysLoaded((surveys) => {
            const feedbackSurvey = surveys.find(s =>
                s.name === 'Product Feedback'
            );

            if (feedbackSurvey && surveyRef.current) {
                posthog.renderSurvey(
                    feedbackSurvey.id,
                    '#feedback-container'
                );
            }
        });
    }, []);

    return (
        <div id="feedback-container" ref={surveyRef}>
            {/* Survey will be rendered here */}
        </div>
    );
}

// Custom trigger button
function SurveyTrigger({ surveyId }) {
    async function handleClick() {
        const { canRender } = await posthog.canRenderSurveyAsync(surveyId);

        if (canRender) {
            posthog.displaySurvey(surveyId, {
                type: DisplaySurveyType.Popover,
                position: SurveyPosition.Center
            });
        }
    }

    return <button onClick={handleClick}>Give Feedback</button>;
}
```

## Common Patterns

### NPS Survey

Display NPS survey after key milestones:

```typescript
async function showNPSSurvey() {
    posthog.getActiveMatchingSurveys((surveys) => {
        const npsSurvey = surveys.find(s =>
            s.type === SurveyType.Popover &&
            s.questions.some(q => q.type === SurveyQuestionType.Rating)
        );

        if (npsSurvey) {
            posthog.displaySurvey(npsSurvey.id, {
                type: DisplaySurveyType.Popover,
                position: SurveyPosition.Center,
                submitButtonText: 'Submit Feedback'
            });
        }
    });
}

// Trigger after significant events
posthog.on('eventCaptured', (data) => {
    if (data.event === 'purchase_completed') {
        // Show NPS survey after first purchase
        showNPSSurvey();
    }
});
```

### Conditional Survey Display

Show different surveys based on user behavior:

```typescript
function showContextualSurvey(context) {
    posthog.getSurveys((surveys) => {
        let surveyToShow;

        switch (context) {
            case 'onboarding_complete':
                surveyToShow = surveys.find(s =>
                    s.name.includes('Onboarding')
                );
                break;
            case 'feature_used':
                surveyToShow = surveys.find(s =>
                    s.name.includes('Feature Feedback')
                );
                break;
            case 'support_needed':
                surveyToShow = surveys.find(s =>
                    s.name.includes('Support')
                );
                break;
        }

        if (surveyToShow) {
            posthog.displaySurvey(surveyToShow.id);
        }
    });
}
```

### Survey Rate Limiting

Prevent survey fatigue with custom rate limiting:

```typescript
const SURVEY_COOLDOWN_KEY = 'last_survey_shown';
const COOLDOWN_DAYS = 7;

async function showSurveyWithRateLimit(surveyId) {
    const lastShown = localStorage.getItem(SURVEY_COOLDOWN_KEY);
    const now = Date.now();

    if (lastShown) {
        const daysSinceLastSurvey = (now - parseInt(lastShown)) / (1000 * 60 * 60 * 24);
        if (daysSinceLastSurvey < COOLDOWN_DAYS) {
            console.log('Survey cooldown active');
            return;
        }
    }

    const { canRender } = await posthog.canRenderSurveyAsync(surveyId);
    if (canRender) {
        posthog.displaySurvey(surveyId);
        localStorage.setItem(SURVEY_COOLDOWN_KEY, now.toString());
    }
}
```

### Multi-Step Survey Flow

Handle multi-question surveys:

```typescript
posthog.onSurveysLoaded((surveys) => {
    const multiStepSurvey = surveys.find(s =>
        s.questions.length > 1
    );

    if (multiStepSurvey) {
        console.log(`Survey has ${multiStepSurvey.questions.length} questions`);

        // Track survey progress
        posthog.on('eventCaptured', (data) => {
            if (data.event === 'survey sent' &&
                data.properties.survey_id === multiStepSurvey.id) {
                console.log('Survey completed:', data.properties);
            }
        });

        posthog.displaySurvey(multiStepSurvey.id);
    }
});
```
