Skip to content

Commit 57fc91a

Browse files
committed
v0.0.3: cursor-based paginated collections (paginatedCollectionOptions + useListView)
1 parent cca7a15 commit 57fc91a

6 files changed

Lines changed: 1153 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.0.3] - 2026-06-03
9+
10+
### Added
11+
12+
- `paginatedCollectionOptions()` for cursor-based paginated TanStack DB collections (`@doeixd/tanstackstart-db/pagination`)
13+
- Forward, backward, or bidirectional pagination via `direction`
14+
- `loadNextPage()` / `loadPreviousPage()` / `refetchFirstPage()` utilities
15+
- Built-in deduplication by `getKey`
16+
- `useListView()` React hook with reactive `[items, loadNext, loadPrevious, state]` tuple
17+
- `useRefetchPaginated()` for stable refresh callbacks
18+
- New subpath export `./pagination` in `package.json`
19+
- Tests: 9 new integration tests for `paginatedCollectionOptions` (initial sync, loadNext, loadPrevious, dedup, refetch, errors, no-op)
20+
821
## [0.0.2] - 2026-06-03
922

1023
### Added
@@ -38,5 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3851
- SSR hydration with confirmed-state snapshots
3952
- Testing utilities: memory DBs, fixtures, mocks, render helpers
4053

54+
[0.0.3]: https://github.com/doeixd/tanstackstart-db/releases/tag/v0.0.3
4155
[0.0.2]: https://github.com/doeixd/tanstackstart-db/releases/tag/v0.0.2
4256
[0.0.1]: https://github.com/doeixd/tanstackstart-db/releases/tag/v0.0.1

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,84 @@ const PostTitle = createDbComponent(db)
501501
.render(({ post }) => <h2>{post.title}</h2>);
502502
```
503503

504+
## Pagination
505+
506+
`@doeixd/tanstackstart-db/pagination` provides cursor-based pagination built on
507+
top of TanStack DB collections. Define a paginated collection once, then use
508+
`useListView` to load pages incrementally with automatic cursor tracking and
509+
reactive re-renders.
510+
511+
```tsx
512+
import { createCollection } from "@tanstack/db";
513+
import { paginatedCollectionOptions, useListView } from "@doeixd/tanstackstart-db/pagination";
514+
515+
const CommentView = db.view("comment", { id: true, body: true, createdAt: true });
516+
517+
const comments = createCollection(
518+
paginatedCollectionOptions({
519+
id: "post-comments",
520+
getKey: (c) => c.id,
521+
pageSize: 10,
522+
cursor: "id",
523+
direction: "both",
524+
fetchPage: async ({ after, before, limit }) => {
525+
const params = new URLSearchParams();
526+
if (after !== undefined) params.set("after", String(after));
527+
if (before !== undefined) params.set("before", String(before));
528+
params.set("limit", String(limit));
529+
const response = await fetch(`/api/comments?${params}`);
530+
return response.json();
531+
},
532+
}),
533+
);
534+
535+
function PostComments() {
536+
const [items, loadNext, loadPrevious, state] = useListView({
537+
collection: comments,
538+
view: CommentView,
539+
});
540+
541+
return (
542+
<div>
543+
{loadPrevious && (
544+
<button onClick={loadPrevious} disabled={state.isLoadingPrevious}>
545+
Load older
546+
</button>
547+
)}
548+
{items.map((comment) => (
549+
<CommentCard key={comment.id} comment={comment} />
550+
))}
551+
{loadNext && (
552+
<button onClick={loadNext} disabled={state.isLoadingNext}>
553+
Load newer
554+
</button>
555+
)}
556+
</div>
557+
);
558+
}
559+
```
560+
561+
The `useListView` hook returns a tuple of `[items, loadNext, loadPrevious, state]`:
562+
563+
- `items` - all currently loaded items projected through the view, reactively updated
564+
- `loadNext` - function to load the next page, or `undefined` if no more pages or a load is in flight
565+
- `loadPrevious` - function to load the previous page (only present for `direction: "backward"` or `"both"`), or `undefined` if at the start or a load is in flight
566+
- `state` - pagination state including `hasNextPage`, `hasPreviousPage`, `isLoadingNext`, `isLoadingPrevious`, `error`, `totalCount`, `loadedCount`
567+
568+
Items are deduplicated by `getKey`, so calling `loadNextPage` twice with the
569+
same cursor is safe. Use `useRefetchPaginated(collection)` to get a stable
570+
refetch callback for refresh buttons (clears all loaded data and re-fetches
571+
the first page).
572+
573+
The collection exposes all pagination primitives via `collection.utils`:
574+
575+
- `loadNextPage()` - fetch the next page
576+
- `loadPreviousPage()` - fetch the previous page (undefined for forward-only)
577+
- `refetchFirstPage()` - clear all data and re-fetch the first page
578+
- `subscribe(callback)` - subscribe to pagination state changes
579+
- `getState()` - get the current pagination state snapshot
580+
- `getCollection()` - get the underlying collection instance
581+
504582
## DB file routes
505583

506584
The main application-level value of the React entrypoint is its DB file-route

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
{
22
"name": "@doeixd/tanstackstart-db",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"description": "Typed schema, view, query, action, route, and hydration helpers for TanStack DB.",
55
"keywords": [
66
"crud",
7+
"cursor",
78
"database",
89
"hydration",
10+
"infinite-scroll",
911
"optimistic",
12+
"pagination",
1013
"react",
1114
"schema",
1215
"ssr",
@@ -32,6 +35,7 @@
3235
"exports": {
3336
".": "./dist/index.mjs",
3437
"./local-storage-collection": "./dist/local-storage-collection.mjs",
38+
"./pagination": "./dist/pagination.mjs",
3539
"./query-collection": "./dist/query-collection.mjs",
3640
"./react": "./dist/react.mjs",
3741
"./schema": "./dist/schema.mjs",

0 commit comments

Comments
 (0)