@opengovsg/confetti
v0.5.2
Published
Widgets for Confetti
Keywords
Readme
@opengovsg/confetti
Embeddable React survey widget for collecting user feedback.
Installation
# npm
npm install @opengovsg/confetti
# yarn
yarn add @opengovsg/confetti
# pnpm
pnpm add @opengovsg/confettiPeer Dependencies
This package requires React 18.2.0+ or React 19:
npm install react@^18.2.0Quick Start
- Add the widget to your page:
import { PopoverConfetti } from '@opengovsg/confetti'
function App() {
return (
<div style={{ position: 'fixed', bottom: '1rem', right: '1rem' }}>
<PopoverConfetti
surveyId="your-survey-id"
publishableKey="your-publishable-key"
metadata={{}}
/>
</div>
)
}The template components load their script bundle and stylesheet from the
Confetti CDN at runtime, so there's no CSS to import — including the
host-rendered trigger button, which TriggerPopoverConfetti styles itself. (The
lower-level @opengovsg/confetti/components primitives and
the @opengovsg/confetti/static opt-out surface render in your own bundle and
still require import '@opengovsg/confetti/confetti.css'.)
Note: You'll need the survey ID and publishable key from your Confetti dashboard. Make sure to whitelist your domain in your team settings.
- Add Confetti to your CSP headers. The widget loads its script and stylesheet from the CDN and calls the API, so allowlist all three:
script-src https://confetti.gov.sg;
style-src https://confetti.gov.sg;
connect-src https://confetti.gov.sg;Proxying requests
Every template component accepts an optional proxyUrl. When set, the widget
sends its API requests to {proxyUrl}/api/v1/cfti/... instead of
https://confetti.gov.sg, and the CDN-backed shells also load their script
and stylesheet from {proxyUrl}/widget/v1/.... Use this to route widget
traffic through your own origin, for example when your CSP disallows third
party hosts.
Template Components
Choose the component that best fits your use case:
| Component | Use Case | Behavior |
| ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------- |
| EmbeddedConfetti | Inline surveys, feedback sections | Shows questions in an inline manner with a submit button |
| ModalConfetti | Interruptive, centered surveys | Centered modal overlay, step-by-step questions, auto-submits |
| PopoverConfetti | Floating feedback widgets | Dismissible popover, step-by-step questions, auto-submits |
| TriggerPopoverConfetti | Feedback launcher button | Renders a trigger button that opens a dismissible popover survey on click; reopenable |
| StepperConfetti | Guided surveys | One question at a time, auto-submits after last question |
Static Components
The @opengovsg/confetti/static subpath ships self-contained builds of the
same template components (EmbeddedConfetti, ModalConfetti,
PopoverConfetti, StepperConfetti) plus the ConfettiTrigger composition
helper. They render entirely from your own bundle with no runtime CDN
dependency, so survey rendering updates only reach your site when you upgrade
the package.
import '@opengovsg/confetti/confetti.css'
import { PopoverConfetti } from '@opengovsg/confetti/static'Unlike the CDN-backed shells, the static components require the stylesheet import shown above.
Visibility Hooks
Control when your survey appears with these hooks:
useVisibleAfterDelay
Show the survey after a delay:
import { useVisibleAfterDelay } from '@opengovsg/confetti'
const { isVisible } = useVisibleAfterDelay({ delay: 5000 }) // 5 secondsuseVisibleAfterScroll
Show the survey after scrolling a certain distance:
import { useVisibleAfterScroll } from '@opengovsg/confetti'
const { isVisible } = useVisibleAfterScroll({ threshold: 500 }) // 500pxuseVisibleAfterPageVisits
Show the survey once the visitor has loaded the current page a given number of times (persisted across sessions via localStorage):
import { useVisibleAfterPageVisits } from '@opengovsg/confetti'
const { isVisible, reset } = useVisibleAfterPageVisits({
visits: 3,
respondent: 'optional-respondent-identifier',
})useVisibleAfterSessionPageVisits
Same as above, but the visit count is scoped to the current browser session via sessionStorage:
import { useVisibleAfterSessionPageVisits } from '@opengovsg/confetti'
const { isVisible, reset } = useVisibleAfterSessionPageVisits({ visits: 3 })Building Blocks
The lower-level primitives that power the template components are published from the @opengovsg/confetti/components subpath. Reach for these to compose your own survey UI.
import {
ConfettiController,
ConfettiProvider,
SurveyContext,
useSurvey,
} from '@opengovsg/confetti/components'Unlike the template components, these primitives render in your own bundle, so import the stylesheet once in your app entry point:
import '@opengovsg/confetti/confetti.css'