ChoiceList not reactive - onChange/onInput events not firing properly

Problem

When using <s-choice-list> component in a pos.product-details.action.render extension, the onChange and onInput event handlers do not fire correctly, preventing reactive state updates. This makes it impossible to use ChoiceList for conditional rendering or dynamic UI updates.

Expected Behavior

The onChange or onInput event handler should fire when a user selects a choice, allowing the component to update state and trigger re-renders.

Actual Behavior

Event handlers either:

  • Never fire when a choice is selected
  • Fire but event.detail.values is undefined or incorrect
  • State updates but conditional rendering does not work properly

Steps to Reproduce

  1. Create a POS extension with target pos.product-details.action.render
  2. Add a <s-choice-list> component with state-controlled values prop
  3. Implement onInput or onChange handler to update state
  4. Use state for conditional rendering
  5. Build and deploy the extension
  6. Try to select different choices - observe that state does not update or UI does not react

Code Example

Action.jsx:

import {render} from 'preact';
import {useState} from 'preact/hooks';

function Extension() {
  const [selectionType, setSelectionType] = useState('individual');

  return (
    <s-page heading="Test">
      <s-choice-list
        values={[selectionType]}
        onInput={(e) => {
          console.log('Event:', e);
          const newType = e?.detail?.values?.[0] || selectionType;
          setSelectionType(newType);
        }}
      >
        <s-choice value="player">Player</s-choice>
        <s-choice value="individual">Individual</s-choice>
        <s-choice value="none">None</s-choice>
      </s-choice-list>

      {/* This conditional rendering does not work */}
      {selectionType === 'player' && (
        <s-text>Player selected</s-text>
      )}
      {selectionType === 'individual' && (
        <s-text>Individual selected</s-text>
      )}
    </s-page>
  );
}

export default async () => {
  render(<Extension />, document.body);
};

Result:

  • State never updates when selecting choices
  • Console shows events are not fired or have incorrect structure
  • Conditional rendering always shows the initial state (displays nothing or wrong content)
  • No reactive UI updates occur

Workaround

Currently, the only workaround is to use <s-button> components with onClick handlers instead of ChoiceList, which works reliably but provides a different UX:

<s-button 
  variant={selectionType === 'player' ? 'primary' : 'secondary'}
  onClick={() => setSelectionType('player')}
>
  Player
</s-button>

Environment

  • API Version: 2025-10
  • Extension Type: POS UI Extension
  • Target: pos.product-details.action.render
  • Framework: Preact
  • Component: s-choice-list / s-choice

Thank you for your feedback and confirmation that I’m not the only one experiencing this issue.

I really appreciate your offer to help. I’m currently building a prototype and will reach out if needed. I’m also hoping Shopify will provide an official fix so we won’t need a workaround in the future.

Best regards,
Marvin

1 Like

Please don’t spam with AI responses to offer work - that’s not the purpose of a community developer forum.

@marvinhuebner - what do you get if you print out: e.currentTarget.value

@bkspace Thanks for your input, I tried it.

Logging suggestion tested: e.currentTarget.value

Results for both onInput and onChange:

  • e.detail.values: null
  • e.currentTarget.value: null
  • e.currentTarget.values: contains the selection (e.g., ["player"], ["individual"])

Code Example:

<s-stack gap="small">
  <s-text>ChoiceList debug</s-text>
  <s-choice-list
    onInput={(e) => {
      console.log('ChoiceList onInput detail.values:', e?.detail?.values);
      console.log('ChoiceList onInput currentTarget.value:', e?.currentTarget && e.currentTarget['value']);
      console.log('ChoiceList onInput currentTarget.values:', e?.currentTarget && e.currentTarget['values']);
    }}
    onChange={(e) => {
      console.log('ChoiceList onChange detail.values:', e?.detail?.values);
      console.log('ChoiceList onChange currentTarget.value:', e?.currentTarget && e.currentTarget['value']);
      console.log('ChoiceList onChange currentTarget.values:', e?.currentTarget && e.currentTarget['values']);
    }}
  >
    <s-choice value="player">Player</s-choice>
    <s-choice value="individual">Individual</s-choice>
    <s-choice value="none">None</s-choice>
  </s-choice-list>
</s-stack>

Console Output:

ChoiceList onInput detail.values: null
ChoiceList onInput currentTarget.value: null
ChoiceList onInput currentTarget.values: ["player"]

ChoiceList onChange detail.values: null
ChoiceList onChange currentTarget.value: null
ChoiceList onChange currentTarget.values: ["player"]

ChoiceList onInput detail.values: null
ChoiceList onInput currentTarget.value: null
ChoiceList onInput currentTarget.values: ["individual"]

ChoiceList onChange detail.values: null
ChoiceList onChange currentTarget.value: null
ChoiceList onChange currentTarget.values: ["individual"]

Is currentTarget.values the intended API for POS s-choice-list, or should detail.values/value be populated? If the latter, a fix (or docs note) would help ensure consistent reactive usage across surfaces.