{"id":1438,"date":"2026-10-19T18:00:00","date_gmt":"2026-10-19T17:00:00","guid":{"rendered":"https:\/\/wade.one\/blog\/?p=1438"},"modified":"2026-10-19T18:00:00","modified_gmt":"2026-10-19T17:00:00","slug":"pagination-needs-a-stable-order-before-it-needs-a-clever-cursor","status":"publish","type":"post","link":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/","title":{"rendered":"Pagination Needs a Stable Order Before It Needs a Clever Cursor"},"content":{"rendered":"<p>Pagination looks simple until the underlying list changes between requests. If you sort only by a value that several records share, or leave the order to whatever the database happens to return, moving to the next page can skip or repeat items. I would settle the ordering contract before choosing the pagination mechanism.<\/p>\n<p>GitHub&#8217;s <a href=\"https:\/\/docs.github.com\/en\/graphql\/guides\/using-pagination-in-the-graphql-api\">GraphQL pagination guide<\/a> is a clear example of following an end cursor and checking whether another page exists. A cursor is useful navigation state, but it is not a magical snapshot of a changing dataset. The API still needs to explain what ordering and consistency a caller can rely on.<\/p>\n<p>For an application list, I want a deterministic sort and a unique tie-breaker. If people change the filters or sort direction, the old cursor should not silently carry on with different assumptions. Page size, empty results and the end of the list also deserve ordinary, predictable behaviour. These are small details that become very visible when somebody is working through hundreds of records.<\/p>\n<p>Offset pagination can be perfectly adequate for a bounded list. Cursor pagination can be a better fit elsewhere. Neither choice excuses an unstable order. Start with what &#8220;the next page&#8221; means to the user, then choose the simplest mechanism that can keep that promise.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pagination looks simple until the underlying list changes between requests.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[13],"tags":[257,268,267],"class_list":["post-1438","post","type-post","status-publish","format-standard","hentry","category-programming","tag-apis","tag-graphql","tag-pagination"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one\" \/>\n<meta property=\"og:description\" content=\"Pagination looks simple until the underlying list changes between requests.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/\" \/>\n<meta property=\"og:site_name\" content=\"wade.one\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-19T17:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/Wade-Logo-cropped.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1518\" \/>\n\t<meta property=\"og:image:height\" content=\"1506\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@wadewomersley\" \/>\n<meta name=\"twitter:site\" content=\"@wadewomersley\" \/>\n<meta name=\"twitter:label1\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Pagination Needs a Stable Order Before It Needs a Clever Cursor\",\"datePublished\":\"2026-10-19T17:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/\"},\"wordCount\":223,\"publisher\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\"},\"keywords\":[\"apis\",\"graphql\",\"pagination\"],\"articleSection\":[\"Programming\"],\"inLanguage\":\"en-GB\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/\",\"name\":\"Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#website\"},\"datePublished\":\"2026-10-19T17:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/10\\\/19\\\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wade.one\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Pagination Needs a Stable Order Before It Needs a Clever Cursor\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/\",\"name\":\"wade.one\",\"description\":\"wade womersley - york based software engineer\",\"publisher\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wade.one\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\",\"name\":\"Wade Womersley\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"contentUrl\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"width\":202,\"height\":200,\"caption\":\"Wade Womersley\"},\"logo\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/","og_locale":"en_GB","og_type":"article","og_title":"Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one","og_description":"Pagination looks simple until the underlying list changes between requests.","og_url":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/","og_site_name":"wade.one","article_published_time":"2026-10-19T17:00:00+00:00","og_image":[{"width":1518,"height":1506,"url":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/Wade-Logo-cropped.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_creator":"@wadewomersley","twitter_site":"@wadewomersley","twitter_misc":{"Estimated reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/#article","isPartOf":{"@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/"},"author":{"name":"","@id":""},"headline":"Pagination Needs a Stable Order Before It Needs a Clever Cursor","datePublished":"2026-10-19T17:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/"},"wordCount":223,"publisher":{"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3"},"keywords":["apis","graphql","pagination"],"articleSection":["Programming"],"inLanguage":"en-GB"},{"@type":"WebPage","@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/","url":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/","name":"Pagination Needs a Stable Order Before It Needs a Clever Cursor - wade.one","isPartOf":{"@id":"https:\/\/wade.one\/blog\/#website"},"datePublished":"2026-10-19T17:00:00+00:00","breadcrumb":{"@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/wade.one\/blog\/2026\/10\/19\/pagination-needs-a-stable-order-before-it-needs-a-clever-cursor\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wade.one\/blog\/"},{"@type":"ListItem","position":2,"name":"Pagination Needs a Stable Order Before It Needs a Clever Cursor"}]},{"@type":"WebSite","@id":"https:\/\/wade.one\/blog\/#website","url":"https:\/\/wade.one\/blog\/","name":"wade.one","description":"wade womersley - york based software engineer","publisher":{"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wade.one\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":["Person","Organization"],"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3","name":"Wade Womersley","image":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","url":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","contentUrl":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","width":202,"height":200,"caption":"Wade Womersley"},"logo":{"@id":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png"}}]}},"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1172,"url":"https:\/\/wade.one\/blog\/2026\/04\/05\/what-makes-an-api-feel-nice-to-work-with\/","url_meta":{"origin":1438,"position":0},"title":"What Makes an API Feel Nice to Work With","author":"","date":"April 5, 2026","format":false,"excerpt":"A good API is not just functional. It is predictable, consistent, and easy to use without a lot of guesswork.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1396,"url":"https:\/\/wade.one\/blog\/2026\/08\/17\/php-8-6-clamp-is-tiny-but-welcome\/","url_meta":{"origin":1438,"position":1},"title":"PHP 8.6 clamp Is Tiny but Welcome","author":"","date":"August 17, 2026","format":false,"excerpt":"PHP 8.6's clamp function removes another tiny helper while keeping range boundaries explicit and readable.","rel":"","context":"In &quot;PHP&quot;","block_context":{"text":"PHP","link":"https:\/\/wade.one\/blog\/category\/php\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1305,"url":"https:\/\/wade.one\/blog\/2026\/05\/17\/ai-coding-agents-need-better-configuration-not-just-better-models\/","url_meta":{"origin":1438,"position":2},"title":"AI Coding Agents Need Better Configuration, Not Just Better Models","author":"","date":"May 17, 2026","format":false,"excerpt":"The next gains from AI coding agents will come from model improvements and from clearer repo instructions, permissions, tests, and workflows.","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/wade.one\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1338,"url":"https:\/\/wade.one\/blog\/2026\/06\/06\/c-sharp-14-extension-members-need-restraint\/","url_meta":{"origin":1438,"position":3},"title":"C# 14 Extension Members Need Restraint","author":"Wade","date":"June 6, 2026","format":false,"excerpt":"C# 14 extension members can reduce helper-class noise, but overuse will make APIs harder to understand.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1399,"url":"https:\/\/wade.one\/blog\/2026\/08\/20\/android-17-lock-free-messagequeue-is-a-real-compatibility-check\/","url_meta":{"origin":1438,"position":4},"title":"Android 17&#8217;s Lock-Free MessageQueue Is a Real Compatibility Check","author":"Wade","date":"August 20, 2026","format":false,"excerpt":"Android 17 improves MessageQueue performance, but apps reflecting into private internals need testing before targeting SDK 37.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1433,"url":"https:\/\/wade.one\/blog\/2026\/10\/14\/an-api-timeout-does-not-mean-the-operation-failed\/","url_meta":{"origin":1438,"position":5},"title":"An API Timeout Does Not Mean the Operation Failed","author":"","date":"October 14, 2026","format":false,"excerpt":"A timeout tells you that a response did not arrive in time.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/comments?post=1438"}],"version-history":[{"count":1,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1438\/revisions"}],"predecessor-version":[{"id":1460,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1438\/revisions\/1460"}],"wp:attachment":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/media?parent=1438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/categories?post=1438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/tags?post=1438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}