diff --git a/CHANGELOG.md b/CHANGELOG.md
index a811e684..b0f1bb5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# master
- Add support for the new Relay `@catch` directive. https://github.com/zth/rescript-relay/pull/549
+- Add support for `usePrefetchableForwardPagination`. https://github.com/zth/rescript-relay/pull/551
+- Remove `useBlockingPagination` since it's being removed from Relay.
# 3.1.0
diff --git a/packages/rescript-relay/__tests__/Test_prefetchablePagination-tests.js b/packages/rescript-relay/__tests__/Test_prefetchablePagination-tests.js
new file mode 100644
index 00000000..4e5a151d
--- /dev/null
+++ b/packages/rescript-relay/__tests__/Test_prefetchablePagination-tests.js
@@ -0,0 +1,94 @@
+const t = require("@testing-library/react");
+const React = require("react");
+const queryMock = require("./queryMock");
+const ReactTestUtils = require("react-dom/test-utils");
+
+const {
+ test_prefetchablePagination,
+} = require("./Test_prefetchablePagination.bs");
+
+describe("Prefetchable pagination", () => {
+ test("prefetching works", async () => {
+ queryMock.mockQuery({
+ name: "TestPrefetchablePaginationQuery",
+ data: {
+ loggedInUser: {
+ __typename: "User",
+ id: "user-1",
+ friendsConnection: {
+ pageInfo: {
+ endCursor: "2",
+ hasNextPage: true,
+ startCursor: "",
+ hasPreviousPage: false,
+ },
+ edges: [
+ {
+ cursor: "1",
+ node: {
+ id: "user-2",
+ __typename: "User",
+ },
+ },
+ {
+ cursor: "2",
+ node: {
+ id: "user-3",
+ __typename: "User",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ queryMock.mockQuery({
+ name: "TestPrefetchablePaginationRefetchQuery",
+ variables: {
+ count: 2,
+ cursor: "2",
+ id: "user-1",
+ },
+ data: {
+ node: {
+ __typename: "User",
+ id: "user-1",
+ friendsConnection: {
+ pageInfo: {
+ endCursor: "4",
+ hasNextPage: false,
+ startCursor: "",
+ hasPreviousPage: false,
+ },
+ edges: [
+ {
+ cursor: "3",
+ node: {
+ id: "user-4",
+ __typename: "User",
+ },
+ },
+ {
+ cursor: "4",
+ node: {
+ id: "user-5",
+ __typename: "User",
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ t.render(test_prefetchablePagination());
+ await t.screen.findByText("user-2, user-3");
+
+ ReactTestUtils.act(() => {
+ t.fireEvent.click(t.screen.getByText("Load more"));
+ });
+
+ await t.screen.findByText("user-2, user-3, user-4, user-5");
+ });
+});
diff --git a/packages/rescript-relay/__tests__/Test_prefetchablePagination.res b/packages/rescript-relay/__tests__/Test_prefetchablePagination.res
new file mode 100644
index 00000000..ee29f5ae
--- /dev/null
+++ b/packages/rescript-relay/__tests__/Test_prefetchablePagination.res
@@ -0,0 +1,67 @@
+module Query = %relay(`
+ query TestPrefetchablePaginationQuery {
+ loggedInUser {
+ ...TestPrefetchablePagination_user
+ }
+ }
+`)
+
+module Fragment = %relay(`
+ fragment TestPrefetchablePagination_user on User
+ @refetchable(queryName: "TestPrefetchablePaginationRefetchQuery")
+ @argumentDefinitions(
+ count: { type: "Int", defaultValue: 2 }
+ cursor: { type: "String" }
+ ) {
+ friendsConnection(
+ first: $count
+ after: $cursor
+ ) @connection(key: "TestPrefetchablePagination_friendsConnection", prefetchable_pagination: true) {
+ edges {
+ node {
+ id
+ }
+ }
+ }
+ }
+`)
+
+module Test = {
+ @react.component
+ let make = () => {
+ let query = Query.use(~variables=())
+ let {edges, hasNext, isLoadingNext, loadNext} = Fragment.usePrefetchableForwardPagination(
+ query.loggedInUser.fragmentRefs,
+ ~bufferSize=2,
+ )
+
+
+
+ {edges
+ ->Belt.Array.keepMap(({node}) => node)
+ ->Belt.Array.map(friend => friend.id)
+ ->Js.Array2.joinWith(", ")
+ ->React.string}
+
+ {hasNext
+ ?
+ : React.null}
+
+ }
+}
+
+@live
+let test_prefetchablePagination = () => {
+ let network = RescriptRelay.Network.makePromiseBased(~fetchFunction=RelayEnv.fetchQuery)
+
+ let environment = RescriptRelay.Environment.make(
+ ~network,
+ ~store=RescriptRelay.Store.make(~source=RescriptRelay.RecordSource.make()),
+ )
+
+
+
+
+}
diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res
new file mode 100644
index 00000000..556ca020
--- /dev/null
+++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res
@@ -0,0 +1,279 @@
+/* @sourceLoc Test_prefetchablePagination.res */
+/* @generated */
+%%raw("/* @generated */")
+module Types = {
+ @@warning("-30")
+
+ type rec response_loggedInUser = {
+ fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestPrefetchablePagination_user]>,
+ }
+ type response = {
+ loggedInUser: response_loggedInUser,
+ }
+ @live
+ type rawResponse = response
+ @live
+ type variables = unit
+ @live
+ type refetchVariables = unit
+ @live let makeRefetchVariables = () => ()
+}
+
+
+type queryRef
+
+module Internal = {
+ @live
+ let variablesConverter: Js.Dict.t>> = %raw(
+ json`{}`
+ )
+ @live
+ let variablesConverterMap = ()
+ @live
+ let convertVariables = v => v->RescriptRelay.convertObj(
+ variablesConverter,
+ variablesConverterMap,
+ Js.undefined
+ )
+ @live
+ type wrapResponseRaw
+ @live
+ let wrapResponseConverter: Js.Dict.t>> = %raw(
+ json`{"__root":{"loggedInUser":{"f":""}}}`
+ )
+ @live
+ let wrapResponseConverterMap = ()
+ @live
+ let convertWrapResponse = v => v->RescriptRelay.convertObj(
+ wrapResponseConverter,
+ wrapResponseConverterMap,
+ Js.null
+ )
+ @live
+ type responseRaw
+ @live
+ let responseConverter: Js.Dict.t>> = %raw(
+ json`{"__root":{"loggedInUser":{"f":""}}}`
+ )
+ @live
+ let responseConverterMap = ()
+ @live
+ let convertResponse = v => v->RescriptRelay.convertObj(
+ responseConverter,
+ responseConverterMap,
+ Js.undefined
+ )
+ type wrapRawResponseRaw = wrapResponseRaw
+ @live
+ let convertWrapRawResponse = convertWrapResponse
+ type rawResponseRaw = responseRaw
+ @live
+ let convertRawResponse = convertResponse
+ type rawPreloadToken<'response> = {source: Js.Nullable.t>}
+ external tokenToRaw: queryRef => rawPreloadToken = "%identity"
+}
+module Utils = {
+ @@warning("-33")
+ open Types
+}
+
+type relayOperationNode
+type operationType = RescriptRelay.queryNode
+
+
+let node: operationType = %raw(json` (function(){
+var v0 = [
+ {
+ "kind": "Literal",
+ "name": "first",
+ "value": 2
+ }
+],
+v1 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": [],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "TestPrefetchablePaginationQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "User",
+ "kind": "LinkedField",
+ "name": "loggedInUser",
+ "plural": false,
+ "selections": [
+ {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "TestPrefetchablePagination_user"
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "type": "Query",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": [],
+ "kind": "Operation",
+ "name": "TestPrefetchablePaginationQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "User",
+ "kind": "LinkedField",
+ "name": "loggedInUser",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": (v0/*: any*/),
+ "concreteType": "UserConnection",
+ "kind": "LinkedField",
+ "name": "friendsConnection",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "UserEdge",
+ "kind": "LinkedField",
+ "name": "edges",
+ "plural": true,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "User",
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ (v1/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "cursor",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "PageInfo",
+ "kind": "LinkedField",
+ "name": "pageInfo",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "endCursor",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "hasNextPage",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": "friendsConnection(first:2)"
+ },
+ {
+ "alias": null,
+ "args": (v0/*: any*/),
+ "filters": null,
+ "handle": "connection",
+ "key": "TestPrefetchablePagination_friendsConnection",
+ "kind": "LinkedHandle",
+ "name": "friendsConnection"
+ },
+ (v1/*: any*/)
+ ],
+ "storageKey": null
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "51afb9846e52cc8eb683cb4382261e77",
+ "id": null,
+ "metadata": {},
+ "name": "TestPrefetchablePaginationQuery",
+ "operationKind": "query",
+ "text": "query TestPrefetchablePaginationQuery {\n loggedInUser {\n ...TestPrefetchablePagination_user\n id\n }\n}\n\nfragment TestPrefetchablePagination_user on User {\n friendsConnection(first: 2) {\n edges {\n ...TestPrefetchablePagination_user__edges\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment TestPrefetchablePagination_user__edges on UserEdge {\n node {\n id\n __typename\n }\n cursor\n}\n"
+ }
+};
+})() `)
+
+@live let load: (
+ ~environment: RescriptRelay.Environment.t,
+ ~variables: Types.variables,
+ ~fetchPolicy: RescriptRelay.fetchPolicy=?,
+ ~fetchKey: string=?,
+ ~networkCacheConfig: RescriptRelay.cacheConfig=?,
+) => queryRef = (
+ ~environment,
+ ~variables,
+ ~fetchPolicy=?,
+ ~fetchKey=?,
+ ~networkCacheConfig=?,
+) =>
+ RescriptRelay.loadQuery(
+ environment,
+ node,
+ variables->Internal.convertVariables,
+ {
+ fetchKey,
+ fetchPolicy,
+ networkCacheConfig,
+ },
+ )
+
+@live
+let queryRefToObservable = token => {
+ let raw = token->Internal.tokenToRaw
+ raw.source->Js.Nullable.toOption
+}
+
+@live
+let queryRefToPromise = token => {
+ Js.Promise.make((~resolve, ~reject as _) => {
+ switch token->queryRefToObservable {
+ | None => resolve(Error())
+ | Some(o) =>
+ open RescriptRelay.Observable
+ let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok())))
+ }
+ })
+}
diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res
new file mode 100644
index 00000000..fc47adf7
--- /dev/null
+++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res
@@ -0,0 +1,349 @@
+/* @sourceLoc Test_prefetchablePagination.res */
+/* @generated */
+%%raw("/* @generated */")
+module Types = {
+ @@warning("-30")
+
+ @live
+ type rec response_node = {
+ @live __typename: string,
+ fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestPrefetchablePagination_user]>,
+ }
+ @live
+ type response = {
+ node: option,
+ }
+ @live
+ type rawResponse = response
+ @live
+ type variables = {
+ count?: int,
+ cursor?: string,
+ @live id: string,
+ }
+ @live
+ type refetchVariables = {
+ count: option