Bug: s-select inside s-modal always shows initially-selected option after opening modal

I have a checkout UI extension using Preact with @shopify/ui-extensions version 2025.10.8. I’m required to add a “select” control inside a modal, but am finding that whenever the modal is opened, the control ignores the currently-selected option and always shows the initially-selected option instead:

Source code for the above example:

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

const options = [0, 1, 2];

const TestApp = () => {
  const [selected, setSelected] = useState(0);

  return (
    <>
      <s-button commandFor="test-modal">Open modal</s-button>
      <s-modal id="test-modal">
        <s-stack>
          <s-text>Selected value: {selected}</s-text>

          <s-select
            label="Select a value"
            value={selected.toString()}
            onChange={({ currentTarget }) => {
              if (!currentTarget) return;
      
              const selected = Number((currentTarget as HTMLSelectElement).value);
              setSelected(selected);
            }}
          >
            {options.map((i) => (
              <s-option
                key={i}
                value={i.toString()}
              >
                {i}
              </s-option>
            ))}
          </s-select>
        </s-stack>
      </s-modal>
    </>
  );
};

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

The selected value in state is updated and rendered correctly inside the modal each time it’s opened, but the select control always shows the initially-selected value after the modal is opened. You can change the initial value of selected in the example above to 1 and the control will always show 1 each time the modal is opened, even if that isn’t the selected value.

This behaviour seems like a bug relating to the way that s-modal and s-select behave together.

Thanks for sharing @AndyPye, our team will look into it.

Adding to this: I’m now working on a similar flow that requires an s-number-field inside a modal and I’m seeing the exact same behaviour with that too, so it seems like a more general issue with inputs inside modals rather than a specific issue with the s-select component.

FWIW, I’ve found a workaround for this issue, which is to explicitly unrender the modal after it’s “closed” (which actually seems to just mean hidden) and re-render it again before showing it. Applying this to the repro code from my initial post gives this:

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

const options = [0, 1, 2];

const TestApp = () => {
  const [selected, setSelected] = useState(0);
  const [shouldRenderModal, setShouldRenderModal] = useState(true);

  return (
    <>
      <s-button commandFor="test-modal">Open modal</s-button>
      {shouldRenderModal && (
        <s-modal
          id="test-modal"
          onAfterHide={() => {
          // Force an unrender + rerender of the modal after it's been closed.
          setShouldRenderModal(false);
          setTimeout(() => {
            setShouldRenderModal(true);
          }, 0);
        }}
        >
          <s-stack>
            <s-text>Selected value: {selected}</s-text>

            <s-select
              label="Select a value"
              value={selected.toString()}
              onChange={({ currentTarget }) => {
                if (!currentTarget) return;
        
                const selected = Number((currentTarget as HTMLSelectElement).value);
                setSelected(selected);
              }}
            >
              {options.map((i) => (
                <s-option
                  key={i}
                  value={i.toString()}
                >
                  {i}
                </s-option>
              ))}
            </s-select>
          </s-stack>
        </s-modal>
      )}
    </>
  );
};

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

The s-select inside the modal now always shows the correct value every time it’s opened.

Glad to hear you figured out a workaround @AndyPye and thanks for posting it here.

Resurrecting this because I’ve encountered this same issue again today and the workaround I was using previously isn’t working for some reason I haven’t understood. At least one underlying issue is that if we make state updates around the time the modal is shown (e.g. using the button that shows the modal to also trigger a state update via its onClick prop, or on a timer initiated via the s-modal’s onShow prop), the state updates are often not reflected in the modal’s content when it’s shown. This results in things like the modal getting stuck displaying a loading state, even when the actual state of the app is correct. We’re having to reconsider an entire flow in our extension now, having already invested time building it, because this behaviour is unavoidable for whatever reason in our scenario. We can’t reliably ensure that the modal’s content updates when it should, so we can’t rely on the modal.

It’d be great if someone could take a look into this and see if they can pinpoint the issue and what the likelihood of a solution in the near future would be, so that we don’t have to discard all the work we’ve done on this flow so far :sweat_smile: :folded_hands: