You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'd like to propose a built-in way for Link to be history-aware: a "Back to X" link should return to the entry the user came from when that entry is X — using the browser's back behavior — instead of always pushing a brand-new history entry. I'm imagining this as a preferBack prop on Link (plus a small hook primitive for custom links), and I'd love feedback on whether this belongs in the router and what the API should look like.
The problem
"Back to X" is one of the most common navigation patterns there is — "Back to issues", "Back to results", "← Posts". Today the idiomatic way to build it is a normal link:
<Linkto="/issues">Back to issues</Link>
The catch: this always pushes a new history entry, even when /issues is exactly the entry the user just came from (/issues → /issues/42 → "Back to issues"). That has two real costs:
Forward history is destroyed. After the click the user sits on a fresh /issues entry; the browser's forward button no longer returns them to /issues/42.
Scroll position is lost. A push lands at the top of a new entry instead of restoring the scroll position the browser had saved for the previous /issues entry — so the user loses their place in a long list.
A genuine "back" would fix both, but you can't unconditionally call history.back() for a "Back to X" link: if the user arrived some other way (deep link, refresh, came from a different page), back would take them somewhere unexpected — or right out of the app. So the behavior has to be conditional: go back if and only if the link's target is the previous entry; otherwise navigate normally.
That decision is genuinely hard to make in userland, because the browser only ever exposes the current history entry — there's no API to ask "what was the previous entry?". Answering that reliably is something the router is uniquely positioned to do, which is why this feels like it belongs in TanStack Router rather than in every app's snippets folder.
What I'm proposing
A preferBack prop on the existing Link:
<Linkto="/issues"preferBack>Back to issues</Link>
Behavior (what it does, not how):
When the link's resolved target is the previous history entry → it goes back instead of pushing.
Otherwise → it behaves exactly like a normal link (push, or replace if you set replace).
When the previous entry can't be determined — a fresh load or a deep link — it simply falls back to a normal push. It degrades gracefully and is never wrong, just occasionally a push when a pop wasn't possible.
It still renders a real <a href>, so keyboard navigation, "copy link address", and middle/modifier-click (open in new tab) all keep working as users expect.
It matches the previous entry by pathname by default (so going back restores the exact prior search params and scroll), with an opt-in stricter mode that also requires the search to match.
For links that aren't a Link (custom buttons, design-system components), the same decision would be available as a small hook so the behavior can be reused.
Benefits
Preserves forward history — the back/forward buttons keep working the way users expect.
Restores native scroll position — leans on the browser's per-entry scroll restoration instead of resetting to the top.
Correct "Back to X" UX from a single prop — no manual history.back() plumbing, no guessing.
Graceful by design — deep links, refreshes, and "arrived from elsewhere" all just work; worst case is today's behavior (a push).
Keeps full anchor semantics — copy-link, open-in-new-tab, keyboard, right-click menu are untouched; only a primary click is intercepted.
Composes with existing Link options and stays opt-in — zero impact on existing links.
Zero-config for the common case, with an opt-in precise-match mode when an app needs it.
Reusable primitive for custom links and buttons, not just Link.
Scroll restoration and view transitions work by default.
Prior art / motivation
Frameworks and browsers already invest heavily in preserving back/forward semantics and per-entry scroll restoration; this proposal is about letting declarative "Back to X" links participate in that instead of fighting it. Because only the router has the context to know whether a target is the previous entry, a first-class option avoids every app re-implementing (and getting subtly wrong) the same conditional-back logic.
Open questions
Does this belong on Link as a prop, or would you prefer it live only as a hook / a helper?
Naming — preferBack is meant to signal the best-effort semantics (it prefers back, falling back to a push). Open to alternatives.
Match precision — pathname-only by default with an opt-in exact (pathname + search) mode. Is that the right surface?
Any appetite for it to also support back-aware behavior for replace, or keep that out of scope?
Status
I have a working prototype at #7700 (behavior + tests + docs) and would be happy to improve on it if the direction looks good. Scoped to React first; the underlying mechanism is framework-agnostic, so Solid/Vue parity would be a natural follow-up. Wanted to align on the what and why here.
Here is an example showing that how a Link with preferBack synergyzes with scroll restoration.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
I'd like to propose a built-in way for
Linkto be history-aware: a "Back to X" link should return to the entry the user came from when that entry is X — using the browser's back behavior — instead of always pushing a brand-new history entry. I'm imagining this as apreferBackprop onLink(plus a small hook primitive for custom links), and I'd love feedback on whether this belongs in the router and what the API should look like.The problem
"Back to X" is one of the most common navigation patterns there is — "Back to issues", "Back to results", "← Posts". Today the idiomatic way to build it is a normal link:
The catch: this always pushes a new history entry, even when
/issuesis exactly the entry the user just came from (/issues→/issues/42→ "Back to issues"). That has two real costs:/issuesentry; the browser's forward button no longer returns them to/issues/42./issuesentry — so the user loses their place in a long list.A genuine "back" would fix both, but you can't unconditionally call
history.back()for a "Back to X" link: if the user arrived some other way (deep link, refresh, came from a different page), back would take them somewhere unexpected — or right out of the app. So the behavior has to be conditional: go back if and only if the link's target is the previous entry; otherwise navigate normally.That decision is genuinely hard to make in userland, because the browser only ever exposes the current history entry — there's no API to ask "what was the previous entry?". Answering that reliably is something the router is uniquely positioned to do, which is why this feels like it belongs in TanStack Router rather than in every app's snippets folder.
What I'm proposing
A
preferBackprop on the existingLink:Behavior (what it does, not how):
replace).<a href>, so keyboard navigation, "copy link address", and middle/modifier-click (open in new tab) all keep working as users expect.For links that aren't a
Link(custom buttons, design-system components), the same decision would be available as a small hook so the behavior can be reused.Benefits
history.back()plumbing, no guessing.Linkoptions and stays opt-in — zero impact on existing links.Link.Prior art / motivation
Frameworks and browsers already invest heavily in preserving back/forward semantics and per-entry scroll restoration; this proposal is about letting declarative "Back to X" links participate in that instead of fighting it. Because only the router has the context to know whether a target is the previous entry, a first-class option avoids every app re-implementing (and getting subtly wrong) the same conditional-back logic.
Open questions
Linkas a prop, or would you prefer it live only as a hook / a helper?preferBackis meant to signal the best-effort semantics (it prefers back, falling back to a push). Open to alternatives.replace, or keep that out of scope?Status
I have a working prototype at #7700 (behavior + tests + docs) and would be happy to improve on it if the direction looks good. Scoped to React first; the underlying mechanism is framework-agnostic, so Solid/Vue parity would be a natural follow-up. Wanted to align on the what and why here.
Here is an example showing that how a Link with
preferBacksynergyzes with scroll restoration.Google.Chrome-Vite.App-000783.mp4
Beta Was this translation helpful? Give feedback.
All reactions