tessl install tessl/npm-posthog-js@1.335.0PostHog Browser JS Library is a comprehensive browser analytics and feature management SDK that enables developers to capture user events, track product analytics, manage feature flags, record session replays, and implement feedback mechanisms like surveys and conversations in web applications.
Surveys allow you to collect user feedback, run NPS surveys, and gather qualitative data directly within your application.
Gets all available surveys for the current user.
/**
* 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:
// 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);Gets surveys that match the current conditions (URL, selector, events, etc.).
/**
* 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:
// 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);Displays a survey with custom display options (popover or inline).
/**
* 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:
// 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'
});Renders a survey at a specific DOM selector (inline display).
/**
* 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:
// 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');
}
}Cancels a survey that's scheduled to display after a delay.
/**
* Cancels a pending time-delayed survey
* @param surveyId - Survey ID to cancel
*/
function cancelPendingSurvey(surveyId: string): void;Usage Examples:
// 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');
}Asynchronously checks if a survey can be rendered and returns the reason.
/**
* 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:
// 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.
}
}Registers a callback that fires when surveys are loaded.
/**
* 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:
// 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);
}
})();/**
* Callback invoked when surveys are loaded
*/
type SurveyCallback = (
surveys: Survey[],
context?: { isLoaded: boolean; error?: string }
) => void;/**
* 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;
};
}/**
* 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',
}/**
* 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;
}/**
* 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',
}/**
* 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;
}/**
* 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',
}/**
* 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;
}/**
* Survey display mode
*/
enum DisplaySurveyType {
/**
* Show as popover overlay
*/
Popover = 'popover',
/**
* Show inline in page
*/
Inline = 'inline',
}/**
* 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;
}/**
* Built-in survey event names
*/
enum SurveyEventName {
SHOWN = 'survey shown',
DISMISSED = 'survey dismissed',
SENT = 'survey sent',
ABANDONED = 'survey abandoned',
}Configure survey behavior during initialization:
// Disable surveys entirely
posthog.init('token', {
disable_surveys: true
});
// Disable automatic survey display (manual control only)
posthog.init('token', {
disable_surveys_automatic_display: true
});Always wait for surveys to be loaded before displaying:
// ✅ 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 loadedCheck if survey can be shown before displaying:
// ✅ 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 metTrack survey events for analytics:
// 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;
}
});Control when surveys are displayed:
// 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);Integrate surveys into your UI:
// 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>;
}Display NPS survey after key milestones:
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();
}
});Show different surveys based on user behavior:
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);
}
});
}Prevent survey fatigue with custom rate limiting:
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());
}
}Handle multi-question surveys:
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);
}
});