Every state gets a URL
Tabs, filters and pages live in the URL: reload, share and Back work.
Guillermo Rauch7 Principles of Rich Web ApplicationsConsider, for example, the typical "infinite pagination scenario". The typical way it's implemented involves capturing the click with JavaScript, requesting some data / HTML, injecting it. Making the history.pushState or replaceState call is an optional step, unfortunately not taken by many.
Guillermo Rauch7 Principles of Rich Web ApplicationsAnd this is why I use the word "break". With the simpler model the web proposed initially, this situation was not in the picture. Every state transition relied on a URL change.
Both demos simulate a browser; this page's real URL never changes.
Reload, share, go Back
Pick a tab, a status and Load more, then Reload or Share.
State in memoryBad
State in the URLGood
"use client";
function useSetParams() {
const router = useRouter();
const params = useSearchParams();
return (patch, mode = "push") => {
const next = new URLSearchParams(params);
for (const [k, v] of Object.entries(patch)) v ? next.set(k, v) : next.delete(k);
router[mode](`?${next}`, { scroll: false });
};
}
set({ tab: "preview", page: null }); // push: Back returns here
set({ page: page + 1 }); // push: "Load more" is a state too
set({ q: e.target.value }, "replace"); // typing: one entry, not ten
Push views, replace typing
Type a branch name, switch tab, then press Back.
Push every keystrokeBad
Replace while typingGood
Why it matters, pros and cons
Why it matters
Reload, share and Back all read the URL. State that never reaches it is state they can't bring back.
Pros
- Reload and shared links land on the exact same view.
- Back steps through views instead of out of the app.
- Support can paste a link to what the customer sees.
Cons
- Open menus and hover states don't belong in history.
- Pushing every keystroke floods Back; replace instead.
- Huge filter sets make ugly URLs; save them under an id.