Understand the browser History API in a Next.js + TypeScript app — back and forward, pushState and replaceState, the typed popstate event, and why you use the next/navigation router instead of the raw API.
Why: every page visit is an entry in the browser's session history. The History API lets you move through those entries from code — the same as the Back and Forward buttons.
history.back() // same as clicking Back
history.forward() // same as clicking Forward
history.go(-2) // jump back two entries
console.log(history.length) // how many entries in this tabWhy: this is what powers single-page apps. pushState adds a new URL to the address bar and history without fetching a new page, so your code can swap the content instead. replaceState does the same but overwrites the current entry rather than adding one.
// add a new entry — Back will return to the previous URL
history.pushState({ page: 2 }, '', '/products?page=2')
// replace the current entry — Back skips it
history.replaceState({ page: 2 }, '', '/products?page=2')Note: pushState does not fire any event. But when the user presses Back or Forward, the browser fires a popstate event carrying the state object you saved — that is your cue to render the right content for the new URL. The event is typed as PopStateEvent.
window.addEventListener('popstate', (event: PopStateEvent) => {
// event.state is whatever you passed to pushState / replaceState
console.log('Navigated to', location.pathname, event.state)
})Note: you almost never touch the raw History API in a Next.js app — the framework wraps it. Use <Link> for navigation, and the useRouter hook (router.push, router.replace, router.back) to navigate from code. usePathname and useSearchParams read the current URL. These keep Next's rendering and prefetching in sync, which a raw history.pushState would bypass.
'use client'
import { useRouter, usePathname } from 'next/navigation'
export default function Pager() {
const router = useRouter()
const pathname = usePathname()
// adds a history entry AND lets Next render the route correctly
return (
<button onClick={() => router.push(`${pathname}?page=2`)}>
Next page
</button>
)
}