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.

1 Like

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

1 Like

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.

1 Like