Migrating to 0.6
Surveys that show every question at once keep their submit button enabled and reveal validation errors after a refused submit, and isCompleted is removed.
Version 0.6 changes how surveys that show every question at once report an unanswered question. Previously the embedded and modal templates hid the submit button until every required question had an answer, and no validation error text was ever rendered. A respondent who missed a question saw a survey that simply would not proceed, with nothing on screen explaining why.
The submit button is now always rendered and enabled. Pressing it on an incomplete survey refuses the submission, reveals the blocking errors, and moves focus to the first unanswered question. Errors then update live as the respondent fixes them.
The happy path. If you embed a drop-in template (EmbeddedConfetti,
ModalConfetti, PopoverConfetti, StepperConfetti), you can upgrade with
no code changes. The new behaviour is built in.
Update the package
Install 0.6
npm install @opengovsg/confetti@^0.6.0
# or
pnpm add @opengovsg/confetti@^0.6.0
# or
yarn add @opengovsg/confetti@^0.6.0Update your code if you build your own survey UI
Only callers importing from @opengovsg/confetti/components need changes.
See Are you affected?.
Are you affected?
No changes if you use a drop-in template. The templates handle the new behaviour internally. You may want to check that your styling still reads well with an error message under a question — see Styling the error message.
You must update your code if you render Embed or drive ConfettiController yourself.
isCompleted is removed from both, errors is now gated on the first submit attempt, and submit() returns whether the submission was accepted.
Building-block changes (@opengovsg/confetti/components)
isCompleted is removed
EmbedProps.isCompleted and ConfettiState.isCompleted are both gone.
Its only realistic use was disabling or hiding a submit control, which is the behaviour this release replaces.
Render an always-enabled submit control and let submit() decide.
Before (0.5):
<ConfettiController>
{({ questions, update, isCompleted, submit, errors }) => (
<>
{/* ...questions... */}
<button onClick={submit} disabled={!isCompleted}>
Submit
</button>
</>
)}
</ConfettiController>After (0.6):
<ConfettiController>
{({ questions, update, submit, errors }) => (
<>
{/* ...questions... */}
<button onClick={submit}>Submit</button>
</>
)}
</ConfettiController>If you used Embed, drop the prop:
// Before (0.5)
<Embed onSubmit={submit} isCompleted={isCompleted}>
// After (0.6)
<Embed onSubmit={submit}>submit() returns whether the submission was accepted
submit() now returns false when it refuses an incomplete or invalid survey, and true when the response was sent.
Anything that should only happen on a real submission — navigation, analytics, closing a container — must be gated on the return value.
const handleSubmit = () => {
if (!submit()) {
// Refused. The blocking errors are now exposed through `errors`.
return
}
trackSurveyCompleted()
}Code that assumed the press always went through will now fire on a refused submit. This is the most likely source of a silent bug when upgrading.
errors is gated on the first submit attempt
ConfettiState.errors previously reflected live validity from the moment the survey rendered.
It is now empty until the respondent first presses submit, and live from then on.
This means you can render it directly without tracking a "has the user tried yet" flag of your own:
<SurveyQuestionFactory
question={question}
error={errors[question.id] || ''}
onChange={(answer) => update({ question: question.position, answer })}
/>You must forward errors into your question components. Without it a refused
submit has nothing to display, and the respondent is back to a button that
appears to do nothing.
If you relied on errors as a live validity signal before any submit — for example to drive a progress indicator — derive that from answers in your own component instead.
Styling the error message
Error text renders inside each question as .cf-error and is revealed by a
data-show-errors attribute the controller sets on its root element once a
submit has been refused.
A new --confetti-error-color custom property sets the colour:
.confetti {
--confetti-error-color: #dc2626;
}Surveys that show one question at a time (popover and stepper) are unchanged in this release. They still disable the Next button while the current answer is invalid.
Validation message changes
Unanswered required rating and single-select questions previously produced raw schema text. They now read as respondent-facing copy:
| Question type | Before | After |
|---|---|---|
| Rating | Invalid input: expected number, received undefined | Please select a rating |
| Single select | Invalid input: expected string, received undefined | Please select an option |
These messages are also returned by the response-submission API, so server-side error handling that matched on the old strings needs updating.
Need help?
- See when to show the survey for examples that use the building blocks.
- Email confetti@open.gov.sg
- Slack #confetti (internal use only)
Was this page helpful?
Migrating to 0.5
Survey rendering now loads from the Confetti CDN by default, with a @opengovsg/confetti/static opt-out, plus provider and proxy changes.
Migrating to 0.7
Stepper and popover surveys can now step back to a previous question. ConfettiState and Flow gain new optional fields for building custom back-navigation UI, and the submit button now sits inside a button row.