Onward is a small auto-pager userscript. When you reach the bottom of a paginated page (search results, forum threads, blog archives, shop listings) it finds the link to the next page, fetches it, and adds that page's results below the ones you're reading. You keep scrolling instead of clicking "Next".
It does the same job as Pagetual and AutoPagerize, but the whole thing is about 1,400 readable lines instead of 13,000. There's no rule database to download. Detection works from the page itself, and you can teach it a site in two clicks when it guesses wrong.
- Install Tampermonkey or Violentmonkey.
- On Chrome, Edge and other Chromium browsers, let the manager run scripts. Managers built on Chrome's user scripts API, Tampermonkey 5.5 and later among them, run nothing until you do, and nothing tells you why.
- If the manager's Details page (from
chrome://extensionsoredge://extensions) has an Allow User Scripts switch, as Chrome 138 and later do, turn it on. - Otherwise turn on Developer mode at the top of the extensions page.
- If the manager's Details page (from
- Open
src/onward.user.jsand accept the install prompt.
The script updates itself from this repo. Firefox needs no extra step.
Userscripts for Safari should work too: Onward keeps its settings through the async GM.* storage that manager offers, and since it has no menu, a small "Onward" button in the corner (and an "Onward…" button on each page bar) opens Settings, where the menu's commands are. That hasn't been tried on a real iPhone or Mac yet.
- Finds the next page without rules. It checks
<link rel="next">first, then scores links and buttons by their label ("Next", "Older posts", 下一页, 次へ, 다음, Следующая and about 80 more), their classes, whether they sit in a pager, and whether the URL is the current?page=Nor/page/N/plus one. Previous-page links, disabled items, carousels and sliders are ruled out. Numbered pagers work too: if page 3 is marked current, the link labelled 4 wins. - Finds the list to extend. It looks for the element whose children repeat (results, posts, rows, cards) and covers the most of the screen, skipping headers, footers, menus and the pager itself. Interleaved rows like Hacker News' title and subtext lines stay together.
- Adds only new content. Items that repeat word for word from page 1 (sticky threads, "sort by" bars, headings) are dropped. If a site sends back a page it already sent, Onward stops.
- Handles the awkward cases. Lists rendered by JavaScript get loaded in a hidden iframe. "Load more" buttons get clicked. Pages that scroll inside a div instead of the window work. Non-UTF-8 pages (Shift_JIS, GBK, windows-1251) are decoded properly, and lazy-loaded images get their real
src. - Survives React and friends. If the site redraws its list and throws away the added pages, Onward switches to putting each page in its own copy of the list, which frameworks leave alone.
- Stays out of the way on sites that already scroll forever. Discourse and Flarum forums are left alone. On other sites Onward watches for a few seconds before its first page and whenever you reach the bottom. If the site adds items by itself, Onward takes its own pages back out and stands by.
- Is polite to servers. At most 40 pages per visit by default. A failed request pauses loading until you click Retry. If added pages don't make the page any longer, it stops rather than loading forever.
Each added page gets a slim bar with the page number, its URL, and buttons to jump to the top, to stop, or to skip to the footer (loading then waits 30 seconds, so you can reach it). Once stopped, the bars offer Resume instead. As you scroll, the address bar follows the page you're reading, so refreshing or sharing the link lands in the right place.
Open your userscript manager's menu on any page:
| Command | What it does |
|---|---|
| Toggle Onward on this site | Turns it off or back on for the current host. With "Run on: only sites I list", it adds the site to the list or takes it off, and a site listed through its parent domain is turned off on its own. |
| Load next page now | Loads the next page without scrolling. It also resumes after you pressed Stop or after a failure, and tells you when the last page has been reached. |
| Load 5 more pages | Loads up to five pages one after another, handy for reading or printing a whole thread. It keeps to the page limit and the gap between requests. |
| Run Onward here anyway | Starts Onward on a page where it stood by because the site seemed to load more by itself. |
| Pick next link and content… | Click the "Next" link, then click one result. Onward saves a rule for the site and restarts. |
| Settings | Opens the settings panel. |
When automatic detection gets a site wrong, the picker is the quickest fix. You can also write rules by hand in Settings. A rule is a JSON object:
[
{
"url": "^https://forum\\.example\\.com/threads/",
"next": "a.pagination-next",
"content": "#posts > .post",
"insert": "",
"mode": "fetch",
"click": false
}
]urlis a regular expression matched against the page address.nextandcontentaccept CSS selectors or XPath (anything starting with/,./,(orid().insertis optional. New pages go before the first element it matches.modeisauto,fetchoriframe. Useiframefor sites that build their lists with scripts.- Set
clicktotruewhennextis a button with no link, such as "Load more". excludeUrlis optional. The rule is skipped on addresses it matches.
A rule only takes over on pages where its selectors actually find something. On other pages of the same site, Onward falls back to finding the next page by itself.
Rule lists in AutoPagerize/wedata format (url, nextLink, pageElement, insertBefore) are understood too. Paste their URLs into "Rule list URLs" and Onward refreshes them weekly. Lists on wedata.net, hoothin.github.io and cdn.jsdelivr.net load straight away. For a list anywhere else, Tampermonkey asks you once whether Onward may connect to that site. Very broad patterns from those lists are skipped so they don't override detection.
| Setting | Default | Notes |
|---|---|---|
| Start loading when this many screens remain | 1.5 | Lower means later, higher means earlier. |
| Maximum pages per visit | 40 | A hard cap per page load. |
| Wait between page requests (ms) | 1000 | However fast you scroll, a site gets at most one page request per this many milliseconds. |
| Show a bar between pages | on | Status bars (loading, errors, end) always show. |
| Update the address bar while scrolling | on | Uses history.replaceState, so the back button isn't flooded. |
| Open links on added pages in a new tab | off | Adds target="_blank" to every link on pages Onward loads. |
| Prefetch the next page while you read | off | Fetches the next page's HTML as soon as the current one lands, so scrolling to the next page is instant. |
| Skip offscreen pages while scrolling | off | Applies content-visibility: auto to old pages in long sessions, so the browser skips rendering them. Wrapped mode only. |
| Loading mode | auto | auto fetches first and falls back to an iframe when needed. |
| Load pages | as I scroll | "when I click" loads nothing as you scroll. The bar at the end of the list gets a "Load page N" button instead, which keeps the footer in reach. |
| Load pages on this site | as set above | The same choice for the site you're on only. |
| Run on | every site | "only sites I list" keeps Onward off everywhere except the sites below. |
| Sites to run on | empty | One host per line, used with "only sites I list". Subdomains are covered. |
| Turned off on these sites | empty | The hosts you turned off with the toggle command, one per line. Delete a line to let Onward run there again. These are exact names, so example.com here leaves www.example.com on. |
| Stay off pages whose path matches | checkout, cart, sign-in, sign-up, account and password pages | A regular expression tested against the page's path (and a single-page app's #/route), case-insensitive. The default leaves listings alone (paths through /tag/, /topics/, /category/ and the like), so /tag/account-security/ still pages. Adding pages to a checkout or sign-in flow can break it. "Run Onward here anyway" still works on these pages. |
| Never run on these hosts | Gmail, Docs, YouTube, X, Facebook and a few more | One host per line. Subdomains are covered. |
At the bottom of the panel, Diagnostics shows what Onward found on the page you're on: how it found the next link and where that leads, the list and how many items it started with, the loading mode, the rule it used and where that rule came from, and the last error. "Copy diagnostics" puts all of it on the clipboard for a bug report. It includes the page's address and the next one, which can hold private details, so read it over before you paste it. Nothing is sent anywhere.
Other scripts on the page can follow what Onward adds. It sends the same events as AutoPagerize, so scripts written for AutoPagerize or weAutoPagerize keep working.
| Event | Sent on | When |
|---|---|---|
onward:page |
window |
After each added page or load-more batch. event.detail is { page, url }, where url is null for a load-more batch. |
GM_AutoPagerizeLoaded |
document |
When Onward starts on a page. |
GM_AutoPagerizeNextPageLoaded |
document |
After each added page. |
AutoPagerize_DOMNodeInserted |
each added element (it bubbles) | As a page goes in. event.detail is that page's address. |
Every added element carries the class autopagerize_page_element (in wrapped mode, the page's copy of the list does).
Onward listens on document for AutoPagerizeDisableRequest, AutoPagerizeEnableRequest and AutoPagerizeToggleRequest, so another script can pause paging and start it again:
window.addEventListener('onward:page', (e) => console.log('page', e.detail.page, 'from', e.detail.url));
document.dispatchEvent(new Event('AutoPagerizeDisableRequest'));The tests need Node 22.22.2 or later on the 22 line, 24.15 or later on the 24 line, or 26 and up (what jsdom 30 supports).
npm install
npm test # detection unit tests (jsdom)
npm run e2e # the real script in headless Chromium against a local fixture site
npm run e2e:isolated # the same, with the script in an isolated world as userscript managers run it
npm run check # syntax check and version consistency
npm run screenshots
npm run smoke # real sites from scripts/smoke-sites.json, checked against what Onward should do there
npm run smoke -- https://news.ycombinator.com/news # or just report on any page
npm run bench -- items_all.json # what a wedata-sized rule list costs (download it from wedata.net first)npm run e2e needs Playwright's Chromium (npx playwright install chromium if it isn't there). npm run smoke loads at most three pages per site and fails only when Onward does something other than expected. A site that shows the headless browser a bot check, or refuses its requests, is reported as not checked, and Google and Bing are on the list as things to check by hand. The fixture site in test/e2e/site.js covers a paginated blog, a windows-1252 forum table, a script-rendered grid, a list that redraws itself, a failing server, an inner scroll container and a load-more button.
The script is one file on purpose. It's wrapped in a small factory so the same code runs in the userscript manager and in Node tests without a build step.
MIT. See LICENSE.

