Getting Started

PollCatch is a lightweight, customizable feedback widget library built with Web Components. It supports multiple widget types including star ratings, reactions, NPS scores, and polls. Use standalone widgets to get started instantly with no account, or create account-managed widgets through the dashboard for persistent data and remote management.

Features

  • Two modes: standalone widgets (no account) and account-managed widgets (dashboard-managed)
  • Multiple widget types: Stars, Reactions, NPS, and Polls
  • Fully customizable via HTML attributes and CSS variables
  • Accessible and responsive by default
  • Real-time statistics and chart visualization
  • WCAG-compliant color contrast
  • Works with any framework or vanilla HTML

Installation

CDN (Recommended)

Add the script tag to your HTML head or body:

html
<script type="module" src="https://cdn.jsdelivr.net/npm/pollcatch@latest"></script>

NPM

bash
npm install pollcatch
# or
pnpm install pollcatch

Standalone vs. Account-Managed Widgets

PollCatch offers two ways to use widgets, depending on whether you have an account.

Standalone Widgets

Standalone widgets require no account. All configuration is done via HTML attributes, and the widget is identified by a name attribute (minimum 5 characters). Data is retained for 30 days.

html
<poll-catch
  type="stars"
  name="my-feedback"
  question="Rate our service"
  num-stars="5"
  star-color="#ffc107"
  show-stats="after-vote">
</poll-catch>

Account-Managed Widgets

Account-managed widgets are created and managed from your PollCatch dashboard. In your HTML, you only need a widget-id attributeโ€”all other settings (question, type, colors, options) are loaded from the server. Update your widget anytime without changing code.

html
<poll-catch type="stars" widget-id="abc123xyz"></poll-catch>

Use the metadata attribute to segment responses by page, variant, or user group. The dashboard shows a per-meta breakdown for account-managed widgets.

html
<!-- Segment responses by page -->
<poll-catch type="stars" widget-id="abc123xyz" metadata="pricing-page"></poll-catch>

Comparison

FeatureStandalone WidgetsAccount-Managed Widgets
Identifiername attributewidget-id attribute
ConfigurationHTML attributesDashboard (remote)
Account requiredNoYes (free)
Data retention30 daysPersistent
Remote updatesNo (change HTML)Yes (via dashboard)
AnalyticsBasic (stats display)Full (dashboard reports)
Metadata segmentationNoYes (metadata attribute)
Domain restrictionsNoYes (project-level whitelist)
Widget pagesNoYes (shareable public URL)
CSV exportNoYes (dashboard)

Note: If both widget-id and name are present, widget-id takes precedence.

Basic Usage

All widgets use the <poll-catch> component with different type attributes:

html
<!-- Star rating -->
<poll-catch type="stars" question="Rate our service" num-stars="5"></poll-catch>

<!-- NPS widget -->
<poll-catch type="nps" question="How likely are you to recommend us?"></poll-catch>

<!-- Poll widget -->
<poll-catch type="poll" question="Favorite color?" options="Red,Blue,Green"></poll-catch>

<!-- Reaction widget -->
<poll-catch type="reaction" question="Was this helpful?" options="๐Ÿ‘,๐Ÿ‘Ž"></poll-catch>

Stars Rating Widget

A flexible star rating component with customizable number of stars and styling.

Example

html
<poll-catch
  type="stars"
  name="satisfaction"
  question="How satisfied are you?"
  num-stars="5"
  star-color="#ffc107"
  show-stats="after-vote">
</poll-catch>

Stars-Specific Attributes

AttributeTypeDefaultDescription
num-starsnumber5Number of stars to display (1-10)
star-colorstring#ffc107Star fill color
star-hover-colorstring-Star hover state color (falls back to star-color)
star-sizestring-Size: xs, sm, md, lg, xl, or CSS value (responsive by default)
iconstringโ˜…Icon to display (emoji or SVG)

Reaction Widget

An emoji-based reaction component for emotional feedback with support for custom icons.

Example

html
<poll-catch
  type="reaction"
  name="article-reaction"
  question="React to this article"
  options="๐Ÿ‘,โค๏ธ,๐Ÿ˜‚,๐Ÿ˜ฎ,๐Ÿ˜ข,๐Ÿ˜ก"
  show-counts="true"
  show-stats="always">
</poll-catch>

Reaction-Specific Attributes

AttributeTypeDefaultDescription
optionsstring๐Ÿ‘,๐Ÿ‘ŽComma-separated reaction options
compactbooleanfalseUse compact mode with popover
popup-positionstringautoPopover position: top, bottom, auto
show-countsbooleanfalseShow count for each reaction
reaction-sizestring-Size: xs, sm, md, lg, xl, or CSS value (defaults to 1em)

NPS Widget

A Net Promoter Score component for collecting loyalty metrics on a 0-10 scale.

Example

html
<poll-catch
  type="nps"
  name="nps-score"
  question="How likely are you to recommend us?"
  min-label="Not likely"
  max-label="Very likely"
  show-stats="after-vote">
</poll-catch>

NPS-Specific Attributes

AttributeTypeDefaultDescription
min-labelstringNot at allLabel for minimum value (0)
max-labelstringExtremelyLabel for maximum value (10)
min-max-positionstringtopLabel position: top or bottom
thank-you-msgstring-Message shown after submission

Poll Widget

A component for single or multiple choice poll questions.

Example

html
<poll-catch
  type="poll"
  name="features-poll"
  question="Which features do you want?"
  options="Dark Mode,Mobile App,API Access,Integrations"
  multiple="true"
  show-stats="always"
  show-chart="true">
</poll-catch>

Poll-Specific Attributes

AttributeTypeDefaultDescription
optionsstring-Comma-separated poll options
multiplebooleanfalseAllow multiple selections
compactbooleanfalseUse compact display mode
button-textstringSubmitText for submit button
thank-you-msgstring-Message after submission

Custom Poll Options with Icons

html
<poll-catch type="poll" name="work-location" question="Where do you prefer to work?">
  <poll-catch-option value="office" icon="๐Ÿข" tooltip="Work from office">Office</poll-catch-option>
  <poll-catch-option value="home" icon="๐Ÿ " tooltip="Work from home">From Home</poll-catch-option>
  <poll-catch-option value="hybrid" icon="๐Ÿ”„" tooltip="Mix of both">Hybrid</poll-catch-option>
</poll-catch>

Universal Attributes

These attributes are available for all widget types.

AttributeTypeDefaultDescription
typestring-Required for standalone widgets. Widget type: stars, reaction, nps, poll. Account-managed widgets load this from the dashboard.
namestring-Unique identifier for standalone widgets (min 5 characters). Not needed when using widget-id.
widget-idstring-UUID of an account-managed widget. When set, configuration is loaded from the dashboard. Takes precedence over name.
questionstring-The main question or label text
readonlybooleanfalseMakes the widget view-only
inlinebooleanfalseCompact trigger that opens popover/bottom-sheet (ignored by reaction widgets)
metadatastring-Segment responses by page, variant, or user group. Account-managed widgets show per-meta breakdown in the dashboard. Max 255 characters.

Display & Layout Attributes

AttributeTypeDefaultDescription
question-positionstringtopPosition: top, bottom, left, right
question-alignstringstartAlignment: left, center, right, start, end
stats-positionstringvariesStatistics position: top, bottom, left, right
stats-alignstringvariesStatistics alignment: left, center, right

Statistics Attributes

AttributeTypeDefaultDescription
show-statsstringneverWhen to show: never, after-vote, always (poll defaults to after-vote)
show-chartbooleanfalseShow bar chart visualization
stats-textstringvariesCustom format with {count}, {avg}, {nps}, {pct}

Stats Text Variables

  • {count} - Total number of votes
  • {avg} - Average rating (stars widget)
  • {nps} - Net Promoter Score (NPS widget)
  • {pct} - Percentage value

Theming Attributes

AttributeTypeDefaultDescription
primary-colorstring#2196f3Main brand color
mute-colorstring#ccccccDisabled state color
text-colorstringbuttontextText color
background-colorstringtransparentBackground color
accent-colorstring#f2b200Accent/highlight color
button-radiusstringsmBorder radius: xs, sm, md, lg, xl, pill
sizestringmdFont size: xs, sm, md, lg, xl, or CSS value
cssstring-Custom CSS to inject into the widget

Metadata Segmentation

The metadata attribute tags each response with a string value (max 255 characters). This lets you track the same widget across different contexts without creating separate widgets.

Use Cases

  • Per-page tracking โ€” identify which page drives the most feedback
  • A/B testing โ€” compare responses across variants
  • User segments โ€” tag by plan tier, role, or cohort

Example

html
<!-- Same widget, different metadata per page -->
<poll-catch type="stars" widget-id="abc123xyz" metadata="homepage"></poll-catch>

<!-- On another page -->
<poll-catch type="stars" widget-id="abc123xyz" metadata="pricing"></poll-catch>

<!-- A/B test variant -->
<poll-catch type="stars" widget-id="abc123xyz" metadata="checkout-v2"></poll-catch>

Behavior

  • The widget automatically fetches stats filtered to its metadata value
  • Submitting with metadata is free on all plans
  • The dashboard "Meta Insights" table (paid plans) shows per-meta counts, averages, and breakdowns

Domain Whitelist

Restrict which domains can submit data through your widgets. This is a project-level setting configured in the dashboard.

Setup

  • Go to Dashboard > Project Settings > Allowed Domains
  • Enter a comma-separated list of domains (e.g. example.com, staging.example.com)
  • The widget renders on all domains, but submissions from unlisted domains are rejected by the server

Note: Domain whitelist is not available for standalone widgets. Only account-managed widgets support this feature.

Widget Pages

Each account-managed widget can have a shareable public page at /pages/{shortId}. This is useful for sharing feedback forms via email, social media, or anywhere you can't embed code.

How to Enable

  • Open the widget detail page in the dashboard
  • Click the Page button and toggle the page to enabled
  • Copy the shareable URL

Customization

  • Page title โ€” displayed at the top of the page
  • Header / footer markdown โ€” add context above or below the widget
  • Background & text color โ€” match your brand
  • Google Fonts โ€” load a custom font family
  • Max width โ€” control the page content width

All responses from the widget page use the same widget-id, so they appear alongside embedded widget responses in your analytics.

Analytics & Reporting

Account-managed widgets come with a full analytics dashboard. Standalone widgets only display basic in-widget stats with 30-day data retention.

Dashboard Features

  • Stats overview โ€” total responses, average rating, NPS score at a glance
  • 30-day response chart โ€” daily response volume over the last month
  • Option breakdown โ€” see how responses distribute across choices
  • Meta insights (paid) โ€” per-metadata counts, averages, and breakdowns
  • Paginated response log โ€” browse individual responses with timestamp, value, metadata, and URL

Standalone vs. Account-Managed

CapabilityStandaloneAccount-Managed
In-widget statsYesYes
Dashboard reportsNoYes
Data retention30 daysPersistent
Meta insightsNoYes (paid)

CSV Export

Export all responses from any account-managed widget as a CSV file directly from the dashboard.

How to Export

  • Open the widget detail page in the dashboard
  • Click the Export CSV button
  • The file downloads as {widget-name}-responses.csv

CSV Columns

  • Date โ€” timestamp of the response
  • Response โ€” the submitted value (rating, option, score)
  • Meta โ€” metadata tag, if provided
  • URL โ€” the page URL where the response was submitted

CSV export is available on all account plans.

CSS Variables

Customize widget appearance using CSS custom properties. All variables are prefixed with --pc-.

Color Variables

css
:root {
  --pc-primary-color: #2196f3;
  --pc-accent-color: #f2b200;
  --pc-text-color: buttontext;
  --pc-mute-color: #cccccc;
  --pc-background-color: transparent;
  --pc-border-color: #dee2e6;
  --pc-hover-color: rgba(0, 0, 0, 0.05);
}

Typography Variables

css
:root {
  --pc-font-size-base: 1rem;
  --pc-font-size-small: clamp(0.6em, 0.7em, 0.8em);
  --pc-font-size-large: clamp(1em, 1.1em, 1.2em);
  --pc-line-height-base: 1.5;
}

Widget-Specific Variables

css
/* Stars Widget */
poll-catch[type="stars"] {
  --pc-star-color: #ffc107;
  --pc-star-hover-color: /* falls back to star-color */;
  --pc-stars-star-size: clamp(1.2em, calc(1.2em + 0.3vw), 2em);
  --pc-stars-hover-scale: 1.15;
}

/* NPS Widget */
poll-catch[type="nps"] {
  --pc-nps-button-width: clamp(1.4em, calc(1.2em + 0.3vw), 2em);
  --pc-nps-button-height: clamp(1.6em, calc(1.5em + 0.5vw), 2.4em);
}

/* Reaction Widget */
poll-catch[type="reaction"] {
  --pc-reaction-size: 1em;
  --pc-button-radius: 990px;
}

Custom CSS Injection

Use the css attribute for advanced customization. Styles are automatically scoped to the widget.

html
<poll-catch
  type="stars"
  question="Custom styled stars"
  css=".stars { gap: 0.5em; } .star { filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); }">
</poll-catch>

Dark Mode

Support dark mode using CSS media queries or a manual toggle:

css
/* Automatic dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --pc-primary-color: #0d6efd;
    --pc-text-color: #ffffff;
    --pc-background-color: #212529;
    --pc-border-color: #495057;
    --pc-mute-color: #6c757d;
  }
}

/* Manual toggle */
[data-theme="dark"] {
  --pc-text-color: #ffffff;
  --pc-background-color: #212529;
}

Responsive Design

Widgets are mobile-first and responsive by default. Use CSS variables for responsive customization:

css
:root {
  --pc-font-size-base: 0.875rem;
}

@media (min-width: 768px) {
  :root {
    --pc-font-size-base: 1rem;
  }
}

@media (min-width: 1024px) {
  :root {
    --pc-font-size-base: 1.125rem;
  }
}

Events Overview

All widgets emit three types of events:

  • init - Fired when the widget is initialized and data is loaded
  • change - Fired whenever the user's selection changes
  • submit - Fired when data is successfully sent to the backend

Event Detail Structure

typescript
interface PollcatchEventDetail {
  widgetType: string;      // 'stars', 'reaction', 'nps', 'poll'
  widgetId?: string;       // The widget-id (account-managed widgets)
  widgetName: string;      // The name attribute
  sessionId?: string;      // Session identifier (on submit)
  timestamp: string;       // Unix timestamp (seconds)
  url: string;             // Current page URL
  numericValue?: number;   // For numeric widgets (stars, NPS)
  textValues?: string[];   // For selection widgets (poll, reaction)
  metadata?: string;       // Custom metadata
}

Capturing Events

JavaScript Event Listeners

javascript
const widget = document.querySelector('poll-catch[type="stars"]');

// Listen for selection changes
widget.addEventListener('change', (e) => {
  const { numericValue, widgetName } = e.detail;
  console.log(`${widgetName} rated: ${numericValue} stars`);
});

// Listen for successful submission
widget.addEventListener('submit', (e) => {
  const { numericValue, widgetName } = e.detail;
  console.log(`${widgetName} submitted: ${numericValue} stars`);

  // Send to your own analytics
  fetch('/api/feedback', {
    method: 'POST',
    body: JSON.stringify(e.detail)
  });
});

Global Event Handling

javascript
// Listen for all widget events on the page
document.addEventListener('submit', (e) => {
  if (e.target.tagName === 'POLL-CATCH') {
    const { widgetType, widgetName, numericValue, textValues } = e.detail;
    console.log(`Widget: ${widgetName} (${widgetType})`, numericValue || textValues);
  }
});

Custom Endpoints

Send feedback data to your own server using the data-endpoint attribute or custom functions.

Using data-endpoint

html
<!-- Send data to your own API -->
<poll-catch
  type="stars"
  name="feedback"
  question="Rate us"
  data-endpoint="https://api.yoursite.com/feedback">
</poll-catch>

Using data-func for Custom Logic

html
<script>
  window.myDataHandler = async (projectKey, widgetName, options) => {
    // Custom data fetching logic
    const response = await fetch(`/api/data/${widgetName}`);
    return await response.json();
  };
</script>

<poll-catch
  type="stars"
  name="custom-widget"
  question="Rate us"
  data-func="myDataHandler">
</poll-catch>

Framework Integration

React

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

function FeedbackWidget() {
  const widgetRef = useRef(null);

  useEffect(() => {
    const widget = widgetRef.current;
    const handleChange = (e) => console.log('Changed:', e.detail);

    widget?.addEventListener('change', handleChange);
    return () => widget?.removeEventListener('change', handleChange);
  }, []);

  return (
    <poll-catch
      ref={widgetRef}
      type="nps"
      name="nps-score"
      question="How likely are you to recommend us?"
      show-stats="after-vote"
    />
  );
}

Vue

vue
<template>
  <poll-catch
    type="poll"
    :name="widgetName"
    :question="pollQuestion"
    :options="pollOptions"
    @change="handleChange"
  />
</template>

<script>
export default {
  data() {
    return {
      widgetName: 'feature-poll',
      pollQuestion: 'Which feature would you like next?',
      pollOptions: 'Dark Mode,Mobile App,API Access',
    };
  },
  methods: {
    handleChange(e) {
      console.log('Poll changed:', e.detail);
    },
  },
};
</script>