FastPoll

FastPoll is an embeddable single- or multiple-choice survey with optional follow-up text and respondent fields.

@sensefolks/fastpollWCAG 2.1 AA<12KB gzipped

What FastPoll Records

Set the question and answer options in your dashboard.

Respondents answer the poll, any follow-up questions, and optional fields before submitting.

Refreshing the page clears unfinished answers.

When should I use FastPoll?

Use FastPoll for a question with a short set of distinct answers.

  • Blog posts and tutorials — "Was this helpful?"
  • Feature announcements — "Interested in this feature?"
  • Release notes — "How do you feel about this update?"
  • Community pages — "What topic should we cover next?"
  • Product pages — "Which feature matters most to you?"
  • Onboarding flows — "What's your primary use case?"

Installation

CDN

html
<script type="module" src="https://unpkg.com/@sensefolks/[email protected]/dist/sf-fastpoll/sf-fastpoll.esm.js"></script>

NPM

bash
npm install @sensefolks/fastpoll

How do I embed FastPoll?

HTML

html
<sf-fastpoll 
  survey-key="your-survey-uuid" 
  completion-message="Thank you for your feedback!"
  enable-events="true">
</sf-fastpoll>

React

jsx
import '@sensefolks/fastpoll';

function App() {
  return (
    <sf-fastpoll 
      survey-key="your-survey-uuid" 
      completion-message="Thank you!">
    </sf-fastpoll>
  );
}

See the embedding tutorial for Next.js, Vue, Nuxt, Angular, Svelte, Astro, and TypeScript setup.

API Reference

Properties

PropertyAttributeTypeDefaultDescription
surveyKeysurvey-keystring—Required. UUID of the survey from your dashboard
completionMessagecompletion-messagestring'Thank you for your response!'Message displayed after successful submission
thankYouMessagethank-you-messagestring—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
enableEventsenable-eventsbooleantrueWhether to emit custom events (sfReady, sfStepChange, sfChoiceSelect, sfSubmit, sfError)

Survey Flow

The dashboard configuration controls these steps:

  1. Poll Step — Single or multiple choice question
  2. Follow-up Step (optional) — Open-ended question triggered by specific choices
  3. Survey Fields (optional) — Ask for name, email, or other configured fields
  4. Completion — Thank you message

Response Data

Submissions include:

  • Selected choices and follow-up responses
  • Survey Field values and Session Data (if configured)
  • Completion time in seconds
  • User agent, timezone, and screen resolution

Custom Events

Listen for poll interactions and lifecycle changes:

Events Reference

EventDescriptionDetail Properties
sfReadySurvey loaded and ready to displaysurveyKey, question, choiceType, choiceCount
sfChoiceSelectUser selected or deselected a choicesurveyKey, selectedChoices, choiceType, lastAction, lastChoice
sfStepChangeUser navigated between survey stepssurveyKey, previousStep, currentStep, currentStepIndex
sfSubmitSurvey submitted successfullysurveyKey, selectedChoices, followUpResponse, completionTimeSeconds
sfErrorError occurred (load, submit, or validation)surveyKey, errorType, errorMessage

Vanilla JavaScript

javascript
const poll = document.querySelector('sf-fastpoll');

// Survey loaded and ready
poll.addEventListener('sfReady', (e) => {
  console.log('Survey ready:', e.detail);
  // { surveyKey, question, choiceType, choiceCount }
});

// User selected/deselected a choice
poll.addEventListener('sfChoiceSelect', (e) => {
  console.log('Choice selected:', e.detail);
  // { surveyKey, selectedChoices, choiceType, lastAction, lastChoice }
});

// User navigated between steps
poll.addEventListener('sfStepChange', (e) => {
  console.log('Step changed:', e.detail);
  // { surveyKey, previousStep, currentStep, currentStepIndex }
});

// Survey submitted successfully
poll.addEventListener('sfSubmit', (e) => {
  console.log('Survey submitted:', e.detail);
  // { surveyKey, selectedChoices, followUpResponse, completionTimeSeconds }
});

// Error occurred
poll.addEventListener('sfError', (e) => {
  console.error('Survey error:', e.detail);
  // { surveyKey, errorType, errorMessage }
});

React

jsx
import { useEffect, useRef } from 'react';

function SurveyWithEvents({ surveyKey }) {
  const pollRef = useRef(null);

  useEffect(() => {
    import('@sensefolks/fastpoll');
    
    const poll = pollRef.current;
    if (!poll) return;

    const handleReady = (e) => {
      console.log('Survey ready:', e.detail);
    };

    const handleSubmit = (e) => {
      console.log('Submitted:', e.detail);
      // Track conversion, show thank you modal, etc.
    };

    const handleChoiceSelect = (e) => {
      // Track user engagement
      analytics.track('poll_choice_selected', e.detail);
    };

    poll.addEventListener('sfReady', handleReady);
    poll.addEventListener('sfSubmit', handleSubmit);
    poll.addEventListener('sfChoiceSelect', handleChoiceSelect);

    return () => {
      poll.removeEventListener('sfReady', handleReady);
      poll.removeEventListener('sfSubmit', handleSubmit);
      poll.removeEventListener('sfChoiceSelect', handleChoiceSelect);
    };
  }, []);

  return (
    <sf-fastpoll 
      ref={pollRef}
      survey-key={surveyKey}
      completion-message="Thanks!">
    </sf-fastpoll>
  );
}

Vue 3

Vue event listeners:

vue
<template>
  <sf-fastpoll 
    ref="pollRef"
    :survey-key="surveyKey"
    completion-message="Thanks!"
    @sfReady="onReady"
    @sfSubmit="onSubmit"
    @sfChoiceSelect="onChoiceSelect"
    @sfStepChange="onStepChange"
    @sfError="onError">
  </sf-fastpoll>
</template>

<script setup>
import { onMounted } from 'vue';

const props = defineProps(['surveyKey']);

onMounted(() => {
  import('@sensefolks/fastpoll');
});

function onReady(e) {
  console.log('Survey ready:', e.detail);
}

function onSubmit(e) {
  console.log('Submitted:', e.detail);
}

function onChoiceSelect(e) {
  console.log('Choice selected:', e.detail);
}

function onStepChange(e) {
  console.log('Step changed:', e.detail);
}

function onError(e) {
  console.error('Error:', e.detail);
}
</script>

Common Use Cases

  • Analytics tracking — Track choice selections and submissions
  • Conditional UI — Show or hide elements based on survey state
  • Error handling — Display custom error messages and diagnostics
  • Progress tracking — Update progress indicators on step changes
  • Conversion tracking — Fire conversion pixels on successful submit

CSS Custom Properties

Set these variables on the component:

PropertyDefaultDescription
--sf-primary#005fccPrimary accent color
--sf-primary-hover#0047a3Primary color hover state
--sf-text-primary#111827Primary text color
--sf-text-secondary#6b7280Secondary/muted text color
--sf-error-color#dc2626Error state color
--sf-card-bg#ffffffCard/container background
--sf-card-border#d1d5dbCard/container border color
--sf-card-radius8pxCard/container border radius
--sf-button-radius6pxButton border radius
--sf-transition150ms easeDefault transition timing
--sf-progress-bg#e5e7ebProgress track color
--sf-progress-fill#005fccProgress fill color
--sf-followup-border#d1d5dbBorder color for the follow-up textarea
--sf-choice-hover-bg#f3f4f6Background color on choice option hover
css
/* Override theme tokens on the component */
sf-fastpoll {
  --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-choice-hover-bg: #f5f3ff;
  --sf-followup-border: #a78bfa;
  --sf-input-disabled-bg: #f9fafb;
}

CSS Parts

Target exposed elements with ::part():

PartDescription
survey-containerOuter wrapper for the entire survey
stepWrapper for each survey step
poll-stepWrapper for the poll step
follow-up-stepWrapper for the follow-up step
survey-fields-stepWrapper for the Survey Fields step
completion-stepWrapper for the completion/thank-you screen
headingAll heading elements
poll-headingThe poll question heading
follow-up-headingThe follow-up question heading
survey-fields-headingThe Survey Fields heading
completion-headingThe completion screen heading
progressTop progress header wrapper
progress-indicatorCompatibility alias for the progress header
progress-labelTask or step count label
progress-textCompatibility alias for the progress label
progress-trackContinuous progress bar track
progress-barCompatibility alias for the progress bar track
progress-fillThe filled portion of the progress bar
choices-containerWrapper for all choice options
choice-optionA single choice option label
choice-labelThe text label for a choice option
radio-inputA radio input element
checkbox-inputA checkbox input element
followup-input-containerWrapper for the follow-up textarea
textareaThe follow-up textarea element
followup-textareaThe follow-up textarea (alias)
other-choice-inputInput field for the "Other" choice option
form-containerWrapper for Survey Fields
form-fieldA single form field wrapper
form-labelA form field label
form-inputA text/email/number input field
inputInput field (alias for form-input)
form-selectA dropdown select field
hcaptcha-containerWrapper for the hCaptcha widget (when enabled)
selectDropdown select (alias for form-select)
required-indicatorThe asterisk indicator for required fields
radio-groupWrapper for a radio button group
radio-optionA single radio option row
radio-labelA radio option label
checkbox-groupWrapper for a checkbox group
checkbox-optionA single checkbox option row
checkbox-labelA checkbox option label
button-containerWrapper for navigation buttons
buttonAll buttons
back-buttonThe "Back" navigation button
next-buttonThe "Next" navigation button
submit-buttonThe "Submit" action button
error-containerWrapper for error state display
error-messageError message text
messageAll message elements (loading, error)
loading-messageLoading state text
announcementsARIA live region for screen reader announcements
brandingSenseFolks branding footer
branding-linkThe anchor link within the branding footer
branding-logoThe SVG logo within the branding footer
css
/* Container & Layout */
sf-fastpoll::part(survey-container) { }
sf-fastpoll::part(step) { }
sf-fastpoll::part(poll-step) { }
sf-fastpoll::part(follow-up-step) { }
sf-fastpoll::part(survey-fields-step) { }
sf-fastpoll::part(completion-step) { }

/* Headings */
sf-fastpoll::part(heading) { }
sf-fastpoll::part(poll-heading) { }
sf-fastpoll::part(follow-up-heading) { }
sf-fastpoll::part(survey-fields-heading) { }
sf-fastpoll::part(completion-heading) { }

/* Progress */
sf-fastpoll::part(progress) { }
sf-fastpoll::part(progress-indicator) { }
sf-fastpoll::part(progress-label) { }
sf-fastpoll::part(progress-text) { }
sf-fastpoll::part(progress-track) { }
sf-fastpoll::part(progress-bar) { }
sf-fastpoll::part(progress-fill) { }

/* Poll Choices */
sf-fastpoll::part(choices-container) { }
sf-fastpoll::part(choice-option) { }
sf-fastpoll::part(choice-label) { }
sf-fastpoll::part(radio-input) { }
sf-fastpoll::part(checkbox-input) { }

/* Follow-up */
sf-fastpoll::part(followup-input-container) { }
sf-fastpoll::part(textarea) { }
sf-fastpoll::part(followup-textarea) { }
sf-fastpoll::part(other-choice-input) { }

/* Form Fields */
sf-fastpoll::part(form-container) { }
sf-fastpoll::part(form-field) { }
sf-fastpoll::part(form-label) { }
sf-fastpoll::part(form-input) { }
sf-fastpoll::part(input) { }
sf-fastpoll::part(select) { }
sf-fastpoll::part(form-select) { }
sf-fastpoll::part(hcaptcha-container) { }
sf-fastpoll::part(required-indicator) { }

/* Radio & Checkbox Groups */
sf-fastpoll::part(radio-group) { }
sf-fastpoll::part(radio-option) { }
sf-fastpoll::part(radio-label) { }
sf-fastpoll::part(checkbox-group) { }
sf-fastpoll::part(checkbox-option) { }
sf-fastpoll::part(checkbox-label) { }

/* Buttons */
sf-fastpoll::part(button-container) { }
sf-fastpoll::part(button) { }
sf-fastpoll::part(next-button) { }
sf-fastpoll::part(back-button) { }
sf-fastpoll::part(submit-button) { }

/* Messages & States */
sf-fastpoll::part(message) { }
sf-fastpoll::part(error-message) { }
sf-fastpoll::part(loading-message) { }
sf-fastpoll::part(error-container) { }
sf-fastpoll::part(troubleshooting-link) { }
sf-fastpoll::part(announcements) { }

/* Branding */
sf-fastpoll::part(branding) { }
sf-fastpoll::part(branding-link) { }
sf-fastpoll::part(branding-logo) { }

Styling Example

Combine theme variables and CSS Parts:

css
/* Theme with CSS custom properties */
sf-fastpoll {
  --sf-primary: #7c3aed;
  --sf-primary-hover: #6d28d9;
  --sf-card-radius: 12px;
  --sf-button-radius: 8px;
  --sf-choice-hover-bg: #f5f3ff;
}

/* Fine-tune individual elements with ::part() */
sf-fastpoll::part(survey-container) {
  max-width: 480px;
  padding: 24px;
  border: 1px solid var(--sf-card-border);
  border-radius: var(--sf-card-radius);
}

sf-fastpoll::part(poll-heading) {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--sf-text-primary);
  margin-bottom: 1rem;
}

sf-fastpoll::part(choice-option) {
  padding: 0.75rem 1rem;
  border-radius: var(--sf-card-radius);
  transition: background-color var(--sf-transition);
}

sf-fastpoll::part(submit-button) {
  background-color: var(--sf-primary);
  color: #ffffff;
  border: none;
  border-radius: var(--sf-button-radius);
  padding: 10px 24px;
  cursor: pointer;
}

sf-fastpoll::part(submit-button):hover {
  background-color: var(--sf-primary-hover);
}

sf-fastpoll::part(followup-textarea) {
  border-color: var(--sf-followup-border);
  border-radius: var(--sf-button-radius);
}

Completion and validation styles

CSS PartElement
success-messageSubmission confirmation
survey-field-error-messageRespondent-field validation feedback
other-choice-rowOther option label and text input

Use --sf-success-bg, --sf-success-border, and --sf-success-text for the confirmation background, border, and text.

Accessibility

FastPoll includes:

  • Keyboard Navigation — Tab through options, Enter/Space to select
  • Screen Readers — ARIA labels, live regions for dynamic updates
  • Focus Management — Visible focus indicators, logical tab order
  • High Contrast — Works with Windows High Contrast Mode
  • Reduced Motion — Respects prefers-reduced-motion
  • Form Validation — Accessible error messages with aria-invalid

Troubleshooting

Component not rendering

  • Verify the survey-key is a valid UUID from your dashboard
  • Check browser console for network errors
  • Make sure the script tag uses type="module"

TypeScript errors in React/Vue

  • Add the type declarations shown in the React section above
  • For Vue, configure isCustomElement in your build config

Styles not applying

  • Use ::part() selectors. Regular CSS selectors won't penetrate Shadow DOM
  • Check that part names match exactly (case-sensitive)

Survey shows "invalid public key"

  • Copy the survey key from your dashboard's Embed tab
  • Make sure the survey is published and active

Browser Support

BrowserVersionNotes
Chrome88+Full support
Firefox85+Full support
Safari14+Full support
Edge88+Full support
IE11Not supportedRequires modern Web Component APIs, fetch, and AbortController