Bug: Polaris web components s-table does not re-render properly in Safari (or iOS app)

I’m trying to rewrite my app’s tables using the new web components s-table, s-table-body, etc. The components don’t seem to update properly in Safari (version 18.6) or in the iOS app when the rows are re-rendered. Here is a minimal example that illustrates the issue – just paste this into the react router template “additional page” and load it up:

import { useMemo, useState } from "react";

const TEST_DATA = [
  { id: "1", name: "Alice Johnson", email: "alice@example.com", amount: 150 },
  { id: "2", name: "Bob Smith", email: "bob@example.com", amount: 250 },
  { id: "3", name: "Carol Davis", email: "carol@example.com", amount: 175 },
  { id: "4", name: "David Wilson", email: "david@example.com", amount: 300 },
  { id: "5", name: "Eve Martinez", email: "eve@example.com", amount: 225 },
  { id: "6", name: "Frank Brown", email: "frank@example.com", amount: 180 },
  { id: "7", name: "Grace Lee", email: "grace@example.com", amount: 320 },
  { id: "8", name: "Henry Taylor", email: "henry@example.com", amount: 195 },
  { id: "9", name: "Iris Anderson", email: "iris@example.com", amount: 275 },
  { id: "10", name: "Jack Thomas", email: "jack@example.com", amount: 160 },
];

export default function TestTable() {
  const [searchValue, setSearchValue] = useState("");

  const filteredData = useMemo(() => {
    if (!searchValue) {
      return TEST_DATA;
    }

    const lowerSearch = searchValue.toLowerCase();
    return TEST_DATA.filter(
      (row) =>
        row.name.toLowerCase().includes(lowerSearch) ||
        row.email.toLowerCase().includes(lowerSearch) ||
        row.amount.toString().includes(lowerSearch),
    );
  }, [searchValue]);

  console.log(
    "Rendered rows:",
    filteredData.length,
    filteredData.map((r) => r.id),
  );

  return (
    <s-page>
      <s-section padding="none">
        <s-table>
          <s-search-field
            label="Search"
            labelAccessibilityVisibility="exclusive"
            slot="filters"
            value={searchValue}
            placeholder="Search by name, email, or amount"
            onInput={(e) => {
              setSearchValue(e.currentTarget.value);
            }}
          />
          <s-table-header-row>
            <s-table-header>Name</s-table-header>
            <s-table-header>Email</s-table-header>
            <s-table-header format="numeric">Amount</s-table-header>
          </s-table-header-row>
          <s-table-body>
            {filteredData.map((row) => (
              <s-table-row key={row.id}>
                <s-table-cell>{row.name}</s-table-cell>
                <s-table-cell>{row.email}</s-table-cell>
                <s-table-cell>{row.amount}</s-table-cell>
              </s-table-row>
            ))}
          </s-table-body>
        </s-table>
      </s-section>
    </s-page>
  );
}

Here’s a brief screen cap that shows the DOM updates when the filtering happens in Safari. Essentially the web components show as they should, so react is doing its job, but the s-table-cell components fail to properly populate their shadow DOM with actual visible elements. Sometimes eventually they show up, but it can take a long while (minutes).

polaris-table-rendering-safari

@Anthony_Frehner (or @Liam-Shopify ?) can you make sure the right people see this? The web components are supposed to be the future, but it’s discouraging they are released but don’t seem to be tested across browsers or in the mobile app with common use cases. I hope you all can fix this quickly. There may be people relying on this in production.

Confirmed the issue. We’ll look into it and get back to you. Thanks for reporting this!

Thanks. I’m waiting on this fix to update my app, so please do let me know.

Fix is live. Thanks!

Awesome, thanks for the fast turn around!

1 Like