Skip to content

fix(ui): fall back when token/chain logo image fails to load#1049

Open
0xkeccak wants to merge 3 commits into
relayprotocol:mainfrom
0xkeccak:fix/token-chain-icon-fallback
Open

fix(ui): fall back when token/chain logo image fails to load#1049
0xkeccak wants to merge 3 commits into
relayprotocol:mainfrom
0xkeccak:fix/token-chain-icon-fallback

Conversation

@0xkeccak

Copy link
Copy Markdown

Problem

In the swap/bridge UI, ChainTokenIcon and ChainIcon render a raw <img> with no error handling. They only guard against the literal missing.png URL, so a token or chain logo whose URL looks valid but fails to load at runtime (404, dead CDN, or CORS-blocked) renders the browser's broken-image placeholder.

ChainTokenIcon already has a perfectly good fallback right below the image — the token-symbol letter avatar — but it never triggers on a load failure, only when the URL is absent or equals missing.png.

Fix

Track an image load error per component via onError:

  • ChainTokenIcon — on image error, fall back to the existing token-symbol avatar instead of a broken image.
  • ChainIcon — on image error, render nothing instead of a broken-image box.

Minimal change, no API surface change.

Testing

  • pnpm --filter @relayprotocol/relay-kit-ui typecheck
  • pnpm --filter @relayprotocol/relay-kit-ui lint
  • pnpm run build:ui
  • Changeset included (@relayprotocol/relay-kit-ui patch).

ChainTokenIcon only guarded against the literal 'missing.png' URL, so a
token logo that 404s or is blocked by CORS rendered a broken-image icon
even though a symbol-letter fallback already exists. ChainIcon had the
same gap for chain logos.

Track an image load error per component via onError and fall back to the
token symbol avatar (ChainTokenIcon) or render nothing (ChainIcon).
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@0xkeccak is attempting to deploy a commit to the Uneven Labs Team on Vercel.

A member of the Team first needs to authorize it.

@pedromcunha

Copy link
Copy Markdown
Member

I think the only additional thing is to make sure it resets when the icon url changes right? Did you test that scenario?

Track the errored src instead of a boolean flag, so when the component
re-renders with a different tokenlogoURI/chain icon URL the fallback
clears and the new logo is attempted. Prevents a previously-broken URL
from suppressing a valid new one on the same component instance.
@0xkeccak

0xkeccak commented Jul 1, 2026

Copy link
Copy Markdown
Author

Good catch. Switched from a boolean flag to tracking the errored src URL, so the guard is now erroredSrc !== tokenlogoURI (and !== iconUrl in ChainIcon). When the URL changes the old errored value no longer matches, so the fallback clears and the new image is retried — resets automatically, no useEffect. Pushed as a follow-up commit.

On testing: I didn't find a component test setup in relay-kit-ui, so I verified with typecheck + lint + build:ui and by reasoning through the URL-change path. Happy to add a regression test if you point me at the preferred setup.

@pedromcunha

Copy link
Copy Markdown
Member

One last request that was flagged from the AI code review:

ChainIcon returning null breaks row alignment in lists (the one change I'd request)

ChainIcon is used in list rows like ChainFilterRow.tsx:115 (<ChainIcon ... width={24} height={24} /> followed by the chain name) and in ChainShortcuts, ChainFilterSidebar, etc. On error the component now renders nothing, so that one row's label shifts left relative to its siblings — a new misalignment the old broken-image at least didn't cause (it still occupied the 24px box).

Minimal fix: keep the outer sized div and hide only the — i.e. leave the return condition as iconUrl ? and change the inner ternary to iconUrl && erroredSrc !== iconUrl ?. Layout stays stable everywhere, including the ChainTokenIcon overlay (though there you'd get an empty bordered chip — if you prefer the overlay to vanish entirely, a neutral placeholder background on the wrapper is the compromise).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants