Net Promoter Score Picker
A Net Promoter Score (NPS) picker is a UI/UX component that allows users to select a score from 0 to 10, representing their likelihood to recommend a product, service, or organization. It is a widely used customer loyalty metric developed by Fred Reichheld and Bain & Company. The component renders as a fieldset with a radiogroup role containing 11 radio buttons, one for each score value. Scores are typically categorized as Detractors (0-6), Passives (7-8), and Promoters (9-10). This headless component provides the semantic structure and accessibility features while allowing consumers to apply their own visual styling.
Example
Show demo markup
<div class="net-promoter-score-picker" role="radiogroup" aria-label="How likely are you to recommend us?"><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">0</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">1</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">2</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">3</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">4</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">5</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">6</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">7</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="true">8</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">9</button><button class="net-promoter-score-picker-button" role="radio" aria-checked="false">10</button></div>
Usage example
<script lang="ts">
import NetPromoterScorePicker from "./NetPromoterScorePicker.svelte";
let score = $state("");
</script>
<NetPromoterScorePicker
label="How likely are you to recommend us?"
bind:value={score}
/>
<p>Selected: {score || "none"}</p>
Show Svelte source
// In your Svelte component:
import NetPromoterScorePicker from "lily-design-system-svelte-headless/components/NetPromoterScorePicker/NetPromoterScorePicker.svelte";
<NetPromoterScorePicker label="Picker" />
Usage
NPS survey question with endpoint labels:
<p>How likely are you to recommend us to a friend or colleague?</p>
<NetPromoterScorePicker label="How likely are you to recommend us?" value={score}>
<NetPromoterScorePickerButton value="0" label="0 - Not at all likely" onclick={handleClick} />
<NetPromoterScorePickerButton value="1" label="1" onclick={handleClick} />
<NetPromoterScorePickerButton value="2" label="2" onclick={handleClick} />
<NetPromoterScorePickerButton value="3" label="3" onclick={handleClick} />
<NetPromoterScorePickerButton value="4" label="4" onclick={handleClick} />
<NetPromoterScorePickerButton value="5" label="5" onclick={handleClick} />
<NetPromoterScorePickerButton value="6" label="6" onclick={handleClick} />
<NetPromoterScorePickerButton value="7" label="7" onclick={handleClick} />
<NetPromoterScorePickerButton value="8" label="8" onclick={handleClick} />
<NetPromoterScorePickerButton value="9" label="9" onclick={handleClick} />
<NetPromoterScorePickerButton value="10" label="10 - Extremely likely" onclick={handleClick} />
</NetPromoterScorePicker>
<p>Selected score: {score}</p>
With a custom radio group name:
<NetPromoterScorePicker label="Rate our service" value={score} name="service-nps" />
Props
label: string (required) -- accessible label for the radiogroup
value: string (default: "") -- currently selected score, bindable
name: string (default: "nps") -- name attribute for the radio group
...restProps: Any additional HTML attributes spread onto the fieldset
Keyboard Interactions
- Arrow keys navigate between radio buttons (native radio group behavior)
- Space selects the focused radio button
- Tab moves focus into/out of the radio group
ARIA
role="radiogroup" on the fieldset
aria-label on the fieldset from the label prop
- Each radio has
aria-label with its numeric value
When to Use
- Use to collect a Net Promoter Score (0-10) from users in feedback forms, surveys, or satisfaction questionnaires.
- Use when you need a standardized NPS input asking "How likely are you to recommend..." on a 0-10 scale.
- Use in customer loyalty surveys where Detractor/Passive/Promoter classification is needed.
- Use when the NPS metric will be reported and benchmarked against industry standards.
When Not to Use
- Do not use for display-only NPS scores -- use NetPromoterScoreView instead.
- Do not use for general 1-5 numeric ratings -- use FiveStarRatingPicker.
- Do not use for satisfaction surveys with face labels -- use FiveFaceRatingPicker.
- Do not use for status indicators -- use RedAmberGreenPicker.
Headless
This headless component provides a <fieldset> with role="radiogroup" containing 11 radio buttons (0-10) with aria-label on each. The consumer provides all visual styling, including button layout, color coding for Detractor/Passive/Promoter ranges, and selected-state appearance.
Styles
The consumer provides all CSS styling. The component renders with a .net-promoter-score-picker class for targeting. No default styles are included — this is a fully headless component.
Testing
- Verify the component renders a
<div> element with class net-promoter-score-picker
- Verify role="radiogroup"` on the fieldset
- Verify aria-label` on the fieldset from the label prop
- Verify Each radio has
aria-label with its numeric value
- Verify keyboard interactions work correctly
- Verify pass-through attributes are applied
Advice
- Designers: Color-code the score ranges (0-6 Detractors, 7-8 Passives, 9-10 Promoters) and add endpoint labels such as "Not at all likely" and "Extremely likely".
- Developers: The
value prop is two-way bindable and stores the selected score as a string ("0" through "10"). Use the name prop to differentiate multiple NPS pickers on one page.
Implementation Notes
- Renders as
<fieldset role="radiogroup"> with 11 radio buttons (0-10)
- Each radio has
aria-label with its numeric value for screen readers
- The
value prop is two-way binding
- Default radio group name is "nps", customizable via
name prop
- Radio values are strings ("0" through "10")
Composition
NetPromoterScorePicker is a compound component that can contain NetPromoterScorePickerButton children. The picker provides the radiogroup container, while each button represents one score value.
NetPromoterScorePicker (role="radiogroup") → NetPromoterScorePickerButton (0-10)
Related components
net-promoter-score-picker-button — a picker button for selecting a 0-10 Net Promoter Score
References
- Net Promoter Score: https://en.wikipedia.org/wiki/Net_promoter_score
- WAI-ARIA Radiogroup Role: https://www.w3.org/TR/wai-aria-1.2/#radiogroup
Canonical contract
Generated from this component's canonical metadata — the machine-checked source the implementations are held to.
Metadata
- Component: net-promoter-score-picker
- PascalCase: NetPromoterScorePicker
- Description: a picker for selecting a 0-10 Net Promoter Score
- Status: stable — exercised in composed page flows under e2e and axe, beyond the per-component checks
- HTML tag: <div>
- CSS class: .net-promoter-score-picker
- Interactive: yes
ARIA
role="radiogroup" on the fieldset aria-label on the fieldset from the label prop - Each radio has
aria-label with its numeric value
Keyboard
- Arrow keys navigate between radio buttons (native radio group behavior)
- Space selects the focused radio button
- Tab moves focus into/out of the radio group
Props
label: string (required) -- accessible label for the radiogroup value: string (default: "") -- currently selected score, bindable name: string (default: "nps") -- name attribute for the radio group ...restProps: Any additional HTML attributes spread onto the fieldset