PricePoint
PricePoint is an embeddable pricing survey for Van Westendorp and Gabor-Granger research.
Choose a Pricing Method
The pricing methods answer different questions:
Van Westendorp
Van Westendorp uses four price questions to estimate perceived price boundaries.
- Too Cheap — At what price would you question the quality?
- Bargain — At what price would it be a great deal?
- Expensive — At what price does it start to seem expensive?
- Too Expensive — At what price is it too expensive to consider?
Curve intersections estimate an acceptable price range. Compare it with costs, positioning, and actual purchases. See the Van Westendorp guide.
Gabor-Granger
Gabor-Granger asks whether respondents would buy at candidate prices to estimate stated demand.
A price ladder defines the candidate prices tested.
Refreshing the page clears unfinished answers.
Survey Phases
The sfPhaseChange event reports the active survey phase:
| Phase | What happens |
|---|---|
loading | Component fetches the survey configuration |
vw | Respondent answers the four Van Westendorp price sensitivity questions |
gg | Respondent evaluates a series of price points on a radio scale |
complete | Survey submitted. The completion screen shows your completionMessage |
error | Something went wrong during load or submission. The error message is displayed |
The survey configuration determines which research phases run.
When should I use PricePoint?
Use PricePoint to research pricing for a defined product or service.
- Pricing pages — Understand willingness to pay before launch
- Upgrade research — Compare price sensitivity near an upgrade decision
- Trial research — Study stated purchase intent before setting a paid offer
- Cancellation flows — Learn the price sensitivity of churning users
- New feature launches — Research the acceptable range for a premium feature
- Market expansion — Test pricing in new markets
Installation
CDN
<script type="module" src="https://unpkg.com/@sensefolks/[email protected]/dist/sf-pricepoint/sf-pricepoint.esm.js"></script>NPM
npm install @sensefolks/pricepointHow do I embed PricePoint?
HTML
<sf-pricepoint
survey-key="your-survey-uuid"
completion-message="Thank you for your feedback!">
</sf-pricepoint>React
import { useEffect } from 'react';
function PricingSurvey({ surveyKey }) {
useEffect(() => {
import('@sensefolks/pricepoint');
}, []);
return (
<sf-pricepoint
survey-key={surveyKey}
completion-message="Thank you for helping us find the right price!">
</sf-pricepoint>
);
}See the embedding tutorial for Next.js, Vue, Nuxt, Angular, Svelte, Astro, and TypeScript setup.
API Reference
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
surveyKey | survey-key | string | — | Required. UUID of the survey from your dashboard |
completionMessage | completion-message | string | 'Thank you for your response!' | Message displayed after successful submission |
thankYouMessage | thank-you-message | string | — | Overrides the saved survey thank-you message |
sessionData | — | Record<string, string | number | boolean> | {} | Complete typed Session Data matching every field in the schema defined during survey creation |
Events
| Event | Detail Type | Description |
|---|---|---|
sfReady | { surveyKey: string; productType: string; phase: string } | Fired when the component has loaded its configuration and is ready |
sfSubmit | { surveyKey: string; phase: string; completionTimeSeconds: number } | Fired on successful survey submission |
sfError | { surveyKey: string; errorType: 'load' | 'submit' | 'validation' | 'config'; errorMessage: string } | Fired when an error occurs during load, validation, or submission |
sfPhaseChange | { phase: string } | Fired when the survey phase changes (loading, vw, gg, complete, error) |
Survey Configuration (Dashboard)
Pricing configuration includes:
| Option | Type | Description |
|---|---|---|
currency | string | Currency code (USD, EUR, GBP, etc.) |
priceType | 'recurring' | 'non-recurring' | One-time or subscription pricing |
recurringBasis | 'monthly' | 'annual' | Billing frequency (if recurring) |
respondentDetails | array | Optional fields to collect (name, email, etc.) |
Response Data
Submissions include:
- Price value entered by the respondent
- Currency and pricing type
- Respondent details (if configured)
- Completion time in seconds
- User agent, timezone, and screen resolution
CSS Custom Properties
Set theme variables on the component:
| Property | Default | Description |
|---|---|---|
--sf-primary | #005fcc | Primary accent color |
--sf-primary-hover | #0047a3 | Primary color hover state |
--sf-text-primary | #111827 | Primary text color |
--sf-text-secondary | #6b7280 | Secondary/muted text color |
--sf-error-color | #dc2626 | Error state color |
--sf-card-bg | #ffffff | Card/container background |
--sf-card-border | #d1d5db | Card/container border color |
--sf-card-radius | 8px | Card/container border radius |
--sf-button-radius | 6px | Button border radius |
--sf-transition | 150ms ease | Default transition timing |
--sf-progress-bg | #e5e7eb | Progress track color |
--sf-progress-fill | #005fcc | Progress fill color |
--sf-font-family | system-ui, sans-serif | Font family |
--sf-spacing-scale | 1 | Spacing multiplier |
Legacy aliases --sf-primary-color, --sf-text-color, --sf-background-color, and --sf-border-radius remain supported.
/* Override theme tokens on the component */
sf-pricepoint {
--sf-primary: #7c3aed;
--sf-primary-hover: #6d28d9;
--sf-button-text: #ffffff;
--sf-error-text: #991b1b;
--sf-error-bg: #fef2f2;
--sf-error-border: #fecaca;
--sf-card-radius: 12px;
--sf-button-radius: 8px;
--sf-progress-bg: #ede9fe;
--sf-progress-fill: #7c3aed;
--sf-font-family: 'Inter', system-ui, sans-serif;
--sf-spacing-scale: 1.2;
}CSS Parts
Target exposed elements with ::part():
| Part | Description |
|---|---|
survey-container | Outer wrapper for the entire survey |
heading | Primary heading elements |
progress-indicator | Phase progress text |
progress-label | Progress label text |
progress-text | Compatibility alias for progress label text |
progress-track | Continuous progress bar track |
progress-bar | Compatibility alias for the progress bar track |
progress-fill | The filled portion of the progress bar |
vw-container | Van Westendorp phase wrapper |
question-container | Wrapper for each question |
question-text | Question text paragraph |
validation-errors | Validation error container |
validation-error | Individual validation error message |
button-container | Wrapper for action buttons |
submit-button | The submit/next action button |
error-container | Wrapper for error state display |
error-message | Error message text |
loading-message | Loading state text |
completion-step | Completion/thank-you screen wrapper |
hcaptcha-container | Wrapper for the hCaptcha widget (when enabled) |
branding | SenseFolks branding wrapper |
branding-link | SenseFolks branding link |
branding-logo | SenseFolks branding logo |
gg-container | Gabor-Granger phase wrapper |
price-display | Price value display span |
radio-group | Wrapper for radio button group |
radio-option | A single radio option row |
radio-option-selected | Applied to the selected radio option (additive with radio-option) |
radio-input | Survey Field radio input |
radio-label | Radio option label text |
survey-fields-container | Survey Fields section wrapper |
form-container | Form container compatibility alias |
survey-fields-heading | Survey Fields section heading |
form-field | Individual Survey Field wrapper |
form-label | Survey Field label |
required-indicator | Required field marker |
survey-field-control | Survey Field input wrapper |
form-input | Text, email, or number Survey Field input |
form-select | Dropdown Survey Field input |
field-error | Survey Field validation error |
checkbox-group | Checkbox Survey Field group |
checkbox-option | Checkbox Survey Field option wrapper |
checkbox-input | Checkbox Survey Field input |
checkbox-label | Checkbox Survey Field label |
announcements | ARIA live region for screen reader announcements |
/* Container & Layout */
sf-pricepoint::part(survey-container) { }
sf-pricepoint::part(vw-container) { }
sf-pricepoint::part(gg-container) { }
sf-pricepoint::part(completion-step) { }
sf-pricepoint::part(hcaptcha-container) { }
sf-pricepoint::part(error-container) { }
sf-pricepoint::part(troubleshooting-link) { }
/* Headings & Progress */
sf-pricepoint::part(heading) { }
sf-pricepoint::part(progress-indicator) { }
sf-pricepoint::part(progress-label) { }
sf-pricepoint::part(progress-text) { }
sf-pricepoint::part(progress-track) { }
sf-pricepoint::part(progress-bar) { }
sf-pricepoint::part(progress-fill) { }
/* Questions */
sf-pricepoint::part(question-container) { }
sf-pricepoint::part(question-text) { }
/* Validation */
sf-pricepoint::part(validation-errors) { }
sf-pricepoint::part(validation-error) { }
/* Price Display */
sf-pricepoint::part(price-display) { }
/* Radio Options (GG phase) */
sf-pricepoint::part(radio-group) { }
sf-pricepoint::part(radio-option) { }
sf-pricepoint::part(radio-option-selected) { }
sf-pricepoint::part(radio-input) { }
sf-pricepoint::part(radio-label) { }
/* Survey Fields */
sf-pricepoint::part(survey-fields-container) { }
sf-pricepoint::part(form-container) { }
sf-pricepoint::part(survey-fields-heading) { }
sf-pricepoint::part(form-field) { }
sf-pricepoint::part(form-label) { }
sf-pricepoint::part(required-indicator) { }
sf-pricepoint::part(survey-field-control) { }
sf-pricepoint::part(form-input) { }
sf-pricepoint::part(form-select) { }
sf-pricepoint::part(field-error) { }
sf-pricepoint::part(checkbox-group) { }
sf-pricepoint::part(checkbox-option) { }
sf-pricepoint::part(checkbox-input) { }
sf-pricepoint::part(checkbox-label) { }
/* Buttons */
sf-pricepoint::part(button-container) { }
sf-pricepoint::part(submit-button) { }
/* Messages & States */
sf-pricepoint::part(error-message) { }
sf-pricepoint::part(loading-message) { }
/* Accessibility */
sf-pricepoint::part(announcements) { }
/* Branding */
sf-pricepoint::part(branding) { }
sf-pricepoint::part(branding-link) { }
sf-pricepoint::part(branding-logo) { }Styling Example
Combine theme variables and CSS Parts:
/* Theme with CSS custom properties */
sf-pricepoint {
--sf-primary: #7c3aed;
--sf-primary-hover: #6d28d9;
--sf-card-radius: 12px;
--sf-button-radius: 8px;
}
/* Fine-tune individual elements with ::part() */
sf-pricepoint::part(survey-container) {
font-family: 'Inter', system-ui, sans-serif;
max-width: 480px;
}
sf-pricepoint::part(vw-container) {
background: #f8fafc;
border: 1px solid var(--color__grey--450);
border-radius: 12px;
padding: 16px;
}
sf-pricepoint::part(question-text) {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 12px;
}
sf-pricepoint::part(price-display) {
margin-top: 12px;
padding: 12px;
background: #f0fdf4;
border-radius: 8px;
color: #166534;
font-weight: 500;
}
sf-pricepoint::part(submit-button) {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
border: none;
padding: 14px 28px;
font-weight: 400;
cursor: pointer;
}
/* Style selected radio option additively */
sf-pricepoint::part(radio-option-selected) {
border-color: #7c3aed;
background: #f5f3ff;
}Completion and validation styles
| CSS Part | Element |
|---|---|
success-message | Submission confirmation |
survey-field-error-message | Respondent-field validation feedback |
completion-heading | Completion message heading |
price-input | Typed price field |
price-preview | Formatted price preview |
text-input | Text input |
validation-message | Price validation feedback |
Use --sf-success-bg, --sf-success-border, and --sf-success-text for the confirmation background, border, and text.
Accessibility
PricePoint includes:
- Keyboard Navigation — Tab through fields, Enter to submit
- Screen Readers — ARIA labels, live regions for price preview
- Input Validation — Accessible error messages with
aria-invalid - Focus Management — Visible focus indicators, logical tab order
- High Contrast — Works with Windows High Contrast Mode
- Reduced Motion — Respects
prefers-reduced-motion
Troubleshooting
Price input not accepting values
- The input accepts numeric values only (decimals allowed)
- Negative values are not permitted
- Check browser console for validation errors
Currency symbol not showing
- Make sure the survey is configured with a valid currency in the dashboard
- The currency is fetched from the server. Check network requests if it's missing
Recurring indicator not visible
- Set
priceTypeto 'recurring' in dashboard settings - Configure
recurringBasisas 'monthly' or 'annual'
Browser Support
| Browser | Version | Notes |
|---|---|---|
| Chrome | 88+ | Full support |
| Firefox | 85+ | Full support |
| Safari | 14+ | Full support |
| Edge | 88+ | Full support |
| IE11 | Not supported | Requires modern Web Component APIs, fetch, and AbortController |