diff --git a/web/components/accounts-table.tsx b/web/components/accounts-table.tsx index 11c07de..9693db7 100644 --- a/web/components/accounts-table.tsx +++ b/web/components/accounts-table.tsx @@ -75,6 +75,14 @@ export default function AccountsTable({ const [createOpen, setCreateOpen] = useState(false); const [toast, setToast] = useState(null); + // iOS Safari (and sometimes Chrome on Android) keeps the document + // scroll position across SPA route changes, so tab-switching from + // /users back to / leaves the user halfway down the page. Force + // scroll-to-top on mount; route transitions remount the table. + useEffect(() => { + window.scrollTo({ top: 0, left: 0, behavior: "instant" }); + }, []); + // Accumulated rows from initial server-side fetch + every loadMore. const [rows, setRows] = useState(initial); const [hasMore, setHasMore] = useState(initialHasMore); diff --git a/web/components/users-table.tsx b/web/components/users-table.tsx index 6b64b73..2d561d5 100644 --- a/web/components/users-table.tsx +++ b/web/components/users-table.tsx @@ -77,6 +77,13 @@ export default function UsersTable({ const [createOpen, setCreateOpen] = useState(false); const [toast, setToast] = useState(null); + // Force scroll-to-top on mount — iOS Safari preserves document scroll + // across SPA route changes, so tab-switching leaves the new page + // halfway down. Route transitions remount the table, so [] deps fire. + useEffect(() => { + window.scrollTo({ top: 0, left: 0, behavior: "instant" }); + }, []); + const [rows, setRows] = useState(initial); const [hasMore, setHasMore] = useState(initialHasMore); const [total, setTotal] = useState(initialTotal);