
The Scroll Position Problem: Why Your App's Feed Forgets Where Users Left Off
You built the feed screen, shipped it, and moved on — and then the support emails started. A user taps into a post, reads it, hits back, and lands right at the top of the list again, scrolling past forty items they already saw to find where they were. It feels like a tiny thing until you watch a real person do it three times in one sitting and quietly close the app. Dolfy.ai exists because this exact category of "small" screen-state problem is where most indie-built apps quietly lose users, and it's rarely a design taste issue — it's a data-modeling issue wearing a UI costume.
Key Takeaways
- Scroll position loss is almost never a rendering bug — it's a symptom of treating a list screen as stateless when its underlying data is not.
- Restoring scroll position correctly requires deciding, at the data-model stage, what "the same list" even means when new items have arrived since the user last looked.
- A feed's list state (scroll offset, loaded page, item order) needs to be cached and keyed the same way its data is fetched, or restoration will drift out of sync within a session or two.
- React Native's
FlatListandFlashListexpose the primitives (onScroll,initialScrollIndex,maintainVisibleContentPosition) for this, but none of them fix a screen whose data layer wasn't designed to support it. - Dolfy's Design OS methodology treats this as a Data Model decision before it's a Screen Design decision, which is why the fix belongs earlier in the process than most teams put it.
Why Does Scrolling Back Reset to the Top in the First Place?
It resets to the top because most feed screens are built to fetch fresh data every time they mount, and "fresh" almost always means "page one, sorted by newest." When a user backs out of a detail screen, React Native's default navigation behavior unmounts and remounts the list, and if your data-fetching logic lives in a useEffect that fires on mount, you've just re-triggered the exact same request that built the screen the first time. The list has no memory of where the user was, because nothing in your app was ever asked to remember it.
This is deceptively easy to miss in early development, because when you're the one testing your own app, you almost never scroll deep enough to notice. Real usage looks different: a user scrolling through 60-80 items, tapping into three or four of them, expecting to return to roughly the same spot each time. That gap between how founders test and how users actually behave is exactly why this bug ships constantly and gets caught in reviews instead of QA.

What Does It Actually Cost to Restore Scroll Position Correctly?
Restoring scroll position isn't free, and pretending otherwise is how teams end up with a fix that works in the demo and breaks in production. There are three real costs, and all three need a decision, not a default.
First is the identity cost: you need a stable way to know "this list, for this user, in this filter state" is the same list as last time, which means your data model needs a cache key — not just an API endpoint. Second is the freshness cost: if five new items arrived while the user was reading a detail screen, do you insert them above the saved position (and shift everything down), or hold them in a "new posts" banner the user pulls to reveal? Both are legitimate product decisions; picking neither is what causes the jarring jump users complain about. Third is the technical cost: React Native's FlatList needs either a correct initialScrollIndex plus fixed or measurable item heights, or the newer maintainVisibleContentPosition prop (also supported by FlashList, the higher-performance list component many teams adopt specifically for feed screens), which anchors scroll position to a specific rendered item rather than a raw pixel offset.
None of that is exotic engineering — a competent React Native developer can implement any of the three approaches in an afternoon. The part that actually takes discipline is deciding, in plain English, what should happen to the list state before a single line of screen code gets written.
How Should You Design the Data Layer Before You Design the Screen?
This is where Dolfy's five-step Design OS methodology earns its keep, because it forces the data model to exist as its own deliverable before screen design starts. A data model, in plain terms, is the structured definition of what your app's information looks like and how pieces of it relate — in this case, not just "a post has a title and an author" but "a feed session has a cache key, a scroll anchor, a last-fetched timestamp, and a pending-new-items count." Skipping this step is exactly how teams end up bolting scroll-restoration logic onto a screen component that was never given anywhere to put it.
Concretely, that means your feed's state shape should look less like a raw API response and more like a small, deliberate object: the array of loaded items, the id of the item that was last visible, the scroll offset within that item, and a flag for whether newer items are waiting. Store that in a lightweight cache — React Query and Zustand are common choices in React Native projects — keyed by the same filter/sort parameters the API call uses. When the user backs into the screen, you check the cache before you fetch, restore the anchor item first, and only then reconcile with a background refresh. That ordering is the whole trick: restore, then refresh, never the other way around.

What Does a Well-Designed Feed Screen Look Like in Practice?
In practice, it looks almost boring, which is the point. The user backs out of a detail screen and the list is exactly where they left it, within a pixel or two, in under 300 milliseconds — fast enough that it reads as "it never left" rather than "it loaded again." If new items arrived, a small pill at the top reads something like "12 new posts" instead of silently reordering the screen underneath the user's thumb. Pull-to-refresh still works exactly as expected for an explicit, user-initiated reset to the top.
None of this requires a large component library or a redesign. It requires a handful of design tokens — the shared, reusable style values (spacing, color, radius, type scale) that keep the "new posts" pill, the list item cards, and the rest of the screen visually consistent with the product's design system — and a screen component that reads its scroll anchor from cached state instead of assuming it starts at zero every time. When the underlying data model already carries a cache key and an anchor field, the component-level implementation in React Native or Flutter is a matter of wiring existing props, not inventing new architecture.
Frequently Asked Questions
Does this apply to Flutter and SwiftUI apps, or only React Native?
The underlying problem — treating list state as ephemeral when it needs to persist across navigation — is framework-agnostic. Flutter's ScrollController and SwiftUI's ScrollViewReader both expose comparable anchor-based restoration primitives. Dolfy's Design OS methodology focuses its component export on React Native with TypeScript and Tailwind-based styling, but the data-model reasoning behind the fix applies the same way regardless of what you build the screen in.
Is FlashList worth adopting just for this problem?
Not just for this, but it's a reasonable upgrade if your feed is already large enough to need virtualization improvements. FlashList improves on FlatList's recycling performance and ships maintainVisibleContentPosition support that makes anchor-based scroll restoration noticeably easier to implement correctly, so if you're already hitting scroll-performance issues, solving both at once is efficient.
How long should scroll position actually persist — for the session, or longer?
For most feed screens, session-length persistence (cleared when the app fully closes, not just backgrounded) is the right default — it matches user expectations without the complexity of syncing scroll state to a backend. Apps with genuinely long, infrequently-refreshed feeds sometimes persist it to local storage across app launches, but that's a deliberate product decision, not a default worth reaching for on day one.
Can Dolfy actually generate the components for this, or just plan them?
Dolfy's output is production-ready React Native and Tailwind components with TypeScript types, generated from the Design Foundation and Screen Design steps of its methodology, previewable directly in Expo Go or Web Preview before you touch a line of code yourself. The scroll-anchor and cache-key fields described above are the kind of structural decision the Data Model step is built to capture so the generated screen component has somewhere correct to read that state from.
Designing Feeds That Remember Where Users Left Off
The fix for lost scroll position was never really about FlatList props — it's about whether your data model was ever asked the question "what does this screen need to remember, and where does that memory live?" Teams that answer it at the data-model stage ship a feed that feels stable on the first try. Teams that skip it end up patching scroll behavior screen by screen, app by app, for as long as the product exists. Dolfy's Design OS methodology puts that question in step two, not step five, precisely so the components you export already know the answer. If you're planning your next screen and want the data model to carry its own weight before you design a single pixel, Dolfy walks through Product Definition, Data Model, Design Foundation, Screen Design, and Export as one connected process, not five disconnected tools.