Skip to content

fix(widget): stop the embedded widget from scrolling the host page on navigation - #893

Open
kimsanaka wants to merge 1 commit into
lifinance:mainfrom
kimsanaka:fix/host-page-scroll-reset
Open

kimsanaka wants to merge 1 commit into
lifinance:mainfrom
kimsanaka:fix/host-page-scroll-reset

Conversation

@kimsanaka

Copy link
Copy Markdown

Which Linear task is linked to this PR?

No Linear task linked. GitHub issue: fixes #885.

Why was it implemented this way?

AppDefault builds the widget's router with no scroll options:

createRouter({
  routeTree,
  history: createMemoryHistory({ initialEntries: ['/'] }),
  defaultPreload: 'intent',
})

router-core calls setupScrollRestoration for every client router, whatever the history. Its onRendered handler ends by scrolling the window to the top unless the navigation passed resetScroll: false (router._scroll.next = next.resetScroll ?? true), and the widget's own navigations never pass it. So opening the From/To token list, selecting a token, settings or route details scrolled the host page back to the top. On a phone, where the widget usually sits below the fold, every tap moved the user away from the widget.

scrollRestoration: () => false is the documented option for this case. In scroll-restoration.js:

if (typeof router.options.scrollRestoration === 'function' && !router.options.scrollRestoration({ location: router.latestLocation })) return

Returning false exits before any scroll work, so nothing touches a window the widget does not own. An embedded widget on a memory history should never scroll the host page.

Alternatives considered:

  • Pass resetScroll: false on each internal navigation. That is a per-call-site fix in a dozen places, and any navigation added later silently reintroduces the bug. The router-level option covers all of them, present and future.
  • Suppress the scroll from the host side. That is the workaround the reporter is already using. The point of the fix is to make it unnecessary.

No public API change, and no change to scrolling inside the widget: its own lists and pages keep their positions.

Visual showcase (Screenshots or Videos)

Not attached. The repro in #885 is window.scrollY going 569 → 0 when the To token list opens in a host page with content above the widget (Chrome, 390×844). The scroll itself is performed by router-core, so the test added here pins the router option instead of replaying the scroll.

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem.
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository.

Verification

  • pnpm --filter @lifi/widget test — 30 files, 321 tests passed
  • pnpm --filter @lifi/widget check:types — clean
  • biome check — clean
  • src/AppDefault.test.tsx pins the option and asserts it returns false. Removing scrollRestoration: () => false makes it fail on that assertion.

…igation

AppDefault creates a TanStack router on a memory history and passed no scroll
options. router-core runs setupScrollRestoration on every client router whatever
the history, and its onRendered handler calls window.scrollTo({ top: 0, left: 0 })
after each navigation unless that navigation passed resetScroll: false. The
widget's own navigations never pass it, so opening the From/To token list,
selecting a token, settings or route details scrolled the host page to the top.
On a phone, where the widget usually sits below the fold, every tap moved the
user away from the widget.

The router is now created with scrollRestoration: () => false. router-core calls
that function from onRendered and returns early when it is false, before any
scroll work, so nothing touches the window the widget does not own.

The test pins both halves: that the option is a function and that it returns
false, plus that the router still uses a memory history with preload by intent.
Removing the option makes it fail.
@changeset-bot

changeset-bot Bot commented Sep 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa94b5e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@lifi/widget Patch
@lifi/widget-checkout Patch
nft-checkout Patch
tanstack-router-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
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.

Embedded widget scrolls the host page to the top on every internal navigation

1 participant