diff --git a/src/routes/docs/tutorials/pagination-react/+layout.svelte b/src/routes/docs/tutorials/pagination-react/+layout.svelte
new file mode 100644
index 0000000000..fb9fb3980f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+layout.svelte
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/routes/docs/tutorials/pagination-react/+layout.ts b/src/routes/docs/tutorials/pagination-react/+layout.ts
new file mode 100644
index 0000000000..562b11506f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+layout.ts
@@ -0,0 +1,11 @@
+import type { LayoutLoad } from './$types';
+
+export const load: LayoutLoad = ({ url }) => {
+ const tutorials = import.meta.glob('./**/*.markdoc', {
+ eager: true
+ });
+ return {
+ tutorials,
+ pathname: url.pathname
+ };
+};
diff --git a/src/routes/docs/tutorials/pagination-react/+page.ts b/src/routes/docs/tutorials/pagination-react/+page.ts
new file mode 100644
index 0000000000..388792ab49
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+page.ts
@@ -0,0 +1,6 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async () => {
+ throw redirect(303, '/docs/tutorials/pagination-react/step-1');
+};
diff --git a/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc
new file mode 100644
index 0000000000..f5557af934
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc
@@ -0,0 +1,32 @@
+---
+layout: tutorial
+title: Pagination deep-dive with React
+description: Dive deep into integrating pagination in a React application using the Appwrite backend
+step: 1
+difficulty: intermediate
+readtime: 30
+---
+
+ In this tutorial, you'll learn how to seamlessly integrate Appwrite's pagination methods into a React application.
+ Proper pagination ensures efficient data display, enhance user experience, and provide a smooth interface for navigating large datasets.
+
+ While this is a React example, the same priciples should apply to any other framework or platform.
+
+# Why pagination? {% #why-pagination %}
+Pagination is critical in web development, especially when working with large datasets.
+It helps users explore and process data without feeling overwhelmed by breaking it down into smaller parts.
+With large data sets, it also lets you improve performance and lower resource use by fetching a small "page" of data to be processed at a time.
+
+# Concepts {% #concepts %}
+1. What is pagination?
+1. Building different pagination UIs in React.
+1. Offset pagination.
+1. Cursor pagination.
+1. Trade-offs between offset and cursor pagination.
+
+# Prerequisites {% #prerequisites %}
+Before diving in, ensure you:
+
+1. Basic knowledge of JavaScript and React.
+2. Basic understanding of Appwrite Databases.
+3. Have the latest version of [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc
new file mode 100644
index 0000000000..c7648bf6ed
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc
@@ -0,0 +1,23 @@
+---
+layout: tutorial
+title: Finalizing and further exploration
+description: Test your pagination Implementation in React with Appwrite and discover more resources to deepen your understanding.
+step: 10
+---
+# Wrapping up {% #wrapping-up %}
+Pagination is a fundamental feature for applications that handle large datasets. By now, you should have a solid understanding of how to implement different pagination methods using Appwrite with React.
+# Test your pagination implementation {% #test-your-pagination-implementation %}
+Before deploying or integrating your pagination into a larger project, it's crucial to test it thoroughly:
+
+ ```sh
+ npm run dev -- --open --port 3000
+ ```
+
+ Navigate to [http://localhost:3000](http://localhost:3000) in your browser to interact with your application. Ensure that both offset and cursor pagination methods work as expected, and data loads correctly as you navigate through pages.
+
+ # Dive deeper {% #dive-deeper %}
+
+ To deepen your understanding and explore more advanced pagination techniques or nuances, refer to the official documentation:
+[Appwrite pagination documentation](/docs/products/databases/pagination).
+
+Remember, the choice between offset and cursor pagination will depend on your application's specific needs. Always consider the size of the dataset, user experience, and performance requirements when making your decision.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc
new file mode 100644
index 0000000000..d3b486bdba
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc
@@ -0,0 +1,28 @@
+---
+layout: tutorial
+title: Create app
+description: Create a React app project and integrate it with Appwrite.
+step: 2
+---
+
+# Create React project {% #create-react-project %}
+
+Create a React app with the `npm create` command from [Vite](https://vitejs.dev/).
+
+```sh
+npm create vite@latest todo-app -- --template react && cd todo-app
+```
+
+# Add dependencies {% #add-dependencies %}
+
+Install the JavaScript Appwrite Web SDK.
+
+```sh
+npm install appwrite dotenv
+```
+
+You can start the development server to watch your app update in the browser as you make changes.
+
+```sh
+npm run dev -- --open --port 3000
+```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc
new file mode 100644
index 0000000000..7436296678
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc
@@ -0,0 +1,63 @@
+---
+layout: tutorial
+title: Set up Appwrite
+description: Import and initialize Appwrite for your React application.
+step: 3
+---
+# Create project {% #create-project %}
+
+Head to the [Appwrite Console](https://cloud.appwrite.io/console).
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+If this is your first time using Appwrite, create an account and create your first project.
+
+Next, under **Add a platform**, choose **Web app**. For the **Hostname**, use "localhost".
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+You can skip optional steps.
+
+# Initialize Appwrite SDK {% #init-sdk %}
+
+To use Appwrite in our React app, we need to identify our project ID. You can find this ID on the **Settings** page.
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+Create a new file `src/lib/appwrite.js` to store our Appwrite-related code.
+
+```js
+import { Client, Databases } from "appwrite";
+
+const client = new Client();
+
+client
+ .setEndpoint("https://cloud.appwrite.io/v1")
+ .setProject("import.meta.env.VITE_APPWRITE_PROJECT_ID");
+
+export const databases = new Databases(client);
+```
+
+# Create environment file {% #create-env-file %}
+Set up the environment configuration for your project to declare project-wide variables like Appwrite project ID, database ID, and collection ID. Create a `.env` and add the declare variables.
+```sh
+VITE_APPWRITE_PROJECT_ID=
+VITE_APPWRITE_DATABASE_ID=
+VITE_APPWRITE_COLLECTION_ID=
+```
+This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.
diff --git a/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc
new file mode 100644
index 0000000000..d7f5f8f485
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc
@@ -0,0 +1,57 @@
+---
+layout: tutorial
+title: Add database
+description: Add a database to your React application using Appwrite Web SDK.
+step: 4
+---
+Before we can talk about pagination, we need to create some mock data to paginate.
+
+# Create a database {% #create-a-database %}
+In Appwrite's console, navigate to the **Databases** page and click **Create database** and name it "Development".
+
+# Create a collection {% #create-collection %}
+To create a collection, head to the **Databases** page, find your database, and click **Create collection**. Name the collection "Todos".
+
+Create the following attributes in your collection.
+
+{% table %}
+* {% width=10 %}
+* Field {% width=10 %}
+* Type {% width=10 %}
+* Required {% width=10 %}
+* Size {% width=10 %}
+* Min {% width=10 %}
+* Max {% width=10 %}
+---
+*
+* taskId
+* Integer
+* Yes
+* -
+* 0
+* 500
+---
+*
+* title
+* String
+* Yes
+* 248
+* -
+* -
+---
+*
+* completed
+* Boolean
+* Yes
+* -
+* -
+* -
+{% /table %}
+
+# Add Permissions {% #add-permissions %}
+1. Go to the **Settings** tab.
+2. Scroll to the **Permissions** section.
+3. Add the **Any** role, ensuring you select both **CREATE** and **READ** permissions.
+{% info title="Any role" %}
+ The **Any** role is used in this tutorial for demonstration purposes. Remember to protect your user's data in production applications by configuring proper [permissions](/docs/advanced/platform/permissions).
+{% /info %}
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc
new file mode 100644
index 0000000000..7923325028
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc
@@ -0,0 +1,66 @@
+---
+layout: tutorial
+title: Environment setup
+description: Setup the environment for seeding the database
+step: 5
+---
+
+# Seeding the collection {% #seeding-the-collection %}
+We'll now create a script that populates documents from a `setup.json` file.Create a new file `./db/setup.js` in the root of your project directory for this purpose.
+
+Insert the following code, make sure that you have `[APPWRITE_DATABASE_ID]` and `[APPWRITE_COLLECTION_ID]` with your respective database and collection IDs in a `.env` file on the root.
+
+```js
+import { Client, Databases, ID } from "appwrite";
+
+import dotenv from "dotenv";
+dotenv.config();
+
+const client = new Client();
+
+client
+ .setEndpoint("https://cloud.appwrite.io/v1")
+ .setProject(process.env.VITE_APPWRITE_PROJECT_ID); // Replace with your project ID
+
+export const databases = new Databases(client);
+
+
+const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos";
+
+async function seedCollection() {
+ const response = await fetch(dataURL);
+ const data = await response.json();
+ console.log("Starting to seed collection...");
+ try {
+ for (let i = 0; i < data.length; i++) {
+ const item = data[i];
+ let promise = await databases.createDocument(
+ process.env.VITE_APPWRITE_DATABASE_ID,
+ process.env.VITE_APPWRITE_COLLECTION_ID,
+ ID.unique(),
+ {
+ taskId: item.id,
+ title: item.title,
+ completed: item.completed,
+ },
+ );
+ console.log(`${promise.title} has been added to the collection`);
+ }
+ console.log("Seeding collection done!");
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+seedCollection();
+```
+
+# Run seed script {% #run-seed-script %}
+
+Open a terminal and run the following command:
+
+```sh
+node ./db/setup.js
+```
+
+You should see the process running in the terminal. Once it's completed, check your collection to verify that it's populated with the data.
diff --git a/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc
new file mode 100644
index 0000000000..d4adfc8a48
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc
@@ -0,0 +1,232 @@
+---
+layout: tutorial
+title: Adding todos
+description: Add todos to your React application using Appwrite.
+step: 6
+---
+# Add todos contexts {% #add-todos-contexts %}
+
+We'll now display our Todos in our app. In React, [context](https://reactjs.org/docs/context.html) allows us to share data across components.
+We'll leverage context and create two custom contexts to manage our todos. One for each pagination method.
+
+Create a new file named `src/lib/context/OffsetProvider.jsx` and insert the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function OffsetProvider(props) {
+ const [todos, setTodos] = useState([]);
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(4)],
+ );
+ setTodos(response.documents);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ init();
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+Also create a new file named `src/lib/context/CursorProvider.jsx` and insert the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function CursorProvider(props) {
+ const [todos, setTodos] = useState([]);
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(4)],
+ );
+ setTodos(response.documents);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ init();
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+We'll expand on these contexts' functionality later.
+
+## Basic Routing {% #basic-routing %}
+The todo app will have a header that displays links to the different pagination methods so you can select each method and view the example in the container below it.
+The `App` fragment returns the `Header` component along with other components wrapped in `OffsetProvider` and `CursorProvider` components.
+These providers are used to provide data to the components that they wrap.
+
+Update `src/App.jsx` with the following code:
+
+```js
+import React from "react";
+import { OffsetProvider } from "./lib/context/OffsetProvider";
+import { CursorProvider } from "./lib/context/CursorProvider";
+import { TodosWithOffsetPagination } from "./pages/TodosWithOffsetPagination";
+import { TodosWithOffsetPaginationAndNumbers } from "./pages/TodosWithOffsetPaginationAndNumbers";
+import { TodosWithBasicCursorPagination } from "./pages/TodosWithBasicCursorPagination";
+import { TodosWithBidirectionalCursorPagination } from "./pages/TodosWithBidirectionalCursorPagination";
+const Header = () => (
+
+ );
+}
+
+export default App;
+```
+
+### Todos page {% #todos-page %}
+
+Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
+
+Create four new files in `src/pages/` folder, name them `TodosWithOffsetPagination`, `TodosWithOffsetPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalCursorPagination`. Then insert this placeholder code in each file:
+
+{% tabs %}
+{% tabsitem #todos-with-offset-pagination title="TodosWithOffsetPagination" %}
+```js
+import { useTodos } from "../lib/context/OffsetProvider";
+export function TodosWithOffsetPagination() {
+ const todos = useTodos();
+ return (
+
+
+
+ );
+}
+```
+{% /tabsitem %}
+{% /tabs %}
+
+We will expand these and add UI next.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc
new file mode 100644
index 0000000000..aaea05250a
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc
@@ -0,0 +1,268 @@
+---
+layout: tutorial
+title: Implementing the offset pagination
+description: Explore offset pagination and how the react app accesses the Appwrite backend using this method.
+step: 7
+---
+In this step, we'll delve into adding the offset pagination method to our app.
+
+# What is offset pagination? {% #what-is-offset-pagination %}
+Offset pagination is widely used in databases and online APIs to extract specific data subsets from a bigger whole. It requires you to tell it where to start (offset) and how many records to get (limit).
+In the context of software applications, offset pagination is a technique that may be employed to facilitate the content navigation by incorporating forward and back buttons on a given page.
+
+With Appwrite, offset pagination is achieved using the `Query.limit()` and `Query.offset()` methods.
+Here's an example using Appwrite's `listDocuments()` function to list the next four tasks:
+
+```js
+const page2 = await databases.listDocuments(
+ '[TODOS_DATABASE_ID]',
+ '[TODOS_COLLECTION_ID]',
+ [
+ Query.limit(4),
+ Query.offset(4)
+ ]
+);
+```
+this will retrieve the next four tasks, starting at task-5
+
+We'll explore different UI patterns for offset pagination and how to implement them in our React app.
+
+# Adding the Pagination Component {% #Adding-the-pagination-component %}
+{% tabs %}
+{% tabsitem #Basic-pagination title="Basic Pagination" %}
+
+Open the file named `src/pages/TodosWithOffsetPagination.jsx`, overwrite it with the following code:
+```js
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/OffsetProvider";
+
+export function TodosWithOffsetPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+This component will only have "Previous" and "Next" buttons.
+{% /tabsitem %}
+{% tabsitem #numbered-pagination title="Numbered Pagination" %}
+Open the file named `src/pages/TodosWithOffsetPaginationAndNumbers.jsx`, overwrite it with the following code:
+```js
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/OffsetProvider";
+
+export function TodosWithOffsetPaginationAndNumbers() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+
+This component will have "Previous" and "Next" buttons along with numbered pages for direct navigation.
+{% /tabsitem %}
+{% /tabs %}
+
+# Update OffsetProvider context {% #update-offsetprovider-context %}
+
+Update the contents of `src/lib/context/OffsetProvider.jsx` with the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function OffsetProvider(props) {
+ const [todos, setTodos] = useState([]);
+ const [offset, setOffset] = useState(0);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(0);
+ const pageLimit = 4;
+ const hasNext = totalPages > currentPage;
+
+ async function loadNext() {
+ if (hasNext) {
+ setOffset(offset + pageLimit);
+ }
+ }
+
+ async function loadPrev() {
+ if (offset >= pageLimit) {
+ setOffset(offset - pageLimit);
+ }
+ }
+
+ async function loadPage(pageNumber) {
+ setOffset((pageNumber - 1) * pageLimit);
+ }
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.offset(offset),
+ ],
+ );
+
+ setTodos(response.documents);
+ setCurrentPage(Math.floor(offset / pageLimit) + 1);
+ setTotalPages(response.total / pageLimit);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ useEffect(() => {
+ init();
+ }, [offset]);
+
+ return (
+ 1,
+ }}>
+ {props.children}
+
+ );
+}
+```
+In this updated context:
+ * We're using `Query.limit()` to set the number of tasks we want to fetch per page.
+ * We're using `Query.offset()` to set the starting point for fetching tasks based on the current page.
+ * We've added `loadNext`, `loadPrev`, and `loadPage` function to handle pagination.
+
+ Now, you can use this context in your components to display todo items and handle pagination using Appwrite's offset pagination methods.
+
+You can now choose between the two offset pagination styles based on your UI/UX preferences.
+
+In the next step, we'll transition from our current pagination method to cursor pagination method.
+
+# See it in action {% #see-it-in-action %}
+Before moving on, take a moment to run your application and see the offset pagination in action. Navigate through the tasks using the "forward" and "back" buttons and observe how the tasks are displayed based on the offset. This hands-on experience will give you a clearer understanding of how offset pagination works in a real-world scenario.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc
new file mode 100644
index 0000000000..d46d52e0c8
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc
@@ -0,0 +1,285 @@
+---
+layout: tutorial
+title: Implementing the cursor pagination
+description: Explore cursor pagination and how the React app accesses the appwrite backend using this method.
+step: 8
+---
+In this step, we'll delve into adding the cursor pagination method to our app.
+
+# What is cursor pagination? {% #what-is-cursor-pagination %}
+Cursor pagination uses a unique identifier(often a timestamp, ID, or another consistently ordered field) for a specific document. This identifer acts as a pointer to the next document we want to start querying from to get the next page of data. For instance, if you have a dataset of blog posts ordered by their creation date and you want to retrieve post after a specific data, you would use that date as the cursor.
+
+With Appwrite, cursor pagination is achieved using the `Query.cursorAfter(lastId)` or the `Query.cursorBefore(firstId)` query methods. Here is an example using Appwrite's `listDocuments()` function to retrieve the next page of documents:
+
+```js
+const page2 = await databases.listDocuments(
+ '[DATABASE_ID]',
+ '[COLLECTION_ID]',
+ [
+ Query.limit(25),
+ Query.cursorAfter(lastId),
+ ]
+);
+```
+Cursor pagination is typically used in scenarios where the dataset is large, and the exact number of total items might not be known upfront. Here are different ways you can utilize cursor pagination:
+
+# Creating the Pagination Component {% #creating-the-pagination-component %}
+{% tabs %}
+{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
+
+Open the file named `src/pages/TodosWithBasicCursorPagination.jsx`, overwrite the content with the following code:
+```js
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/CursorProvider";
+
+export function TodosWithBasicCursorPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+
+```
+This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
+{% /tabsitem %}
+{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
+Open the file named `src/pages/TodosWithBidirectionalCursorPagination.jsx`, overwrite the content with the following code:
+```js
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/CursorProvider";
+
+export function TodosWithBidirectionalCursorPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+Bidirectional cursor pagination allows users to navigate through datasets both forwards and backwards using a cursor to maintain position, providing greater flexibility in exploring data. On the other hand, regular cursor pagination typically only supports forward navigation through the dataset, limiting the user's ability to easily access or revisit previous data entries.
+{% /tabsitem %}
+{% /tabs %}
+
+# Update CursorProvider context {% #updating-cursorprovider-context %}
+
+Update the contents of `src/lib/context/CursorProvider.jsx` with the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function CursorProvider(props) {
+ const [todos, setTodos] = useState([]);
+ const [lastId, setLastId] = useState(null);
+ const [firstId, setFirstId] = useState(null);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const pageLimit = 4;
+ const hasMore = totalPages > currentPage;
+
+ async function loadMore() {
+ if (todos.length === 0 || (hasMore && !loading)) {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorAfter(lastId),
+ ],
+ );
+ setCurrentPage((prevPage) => prevPage + 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos((prevTodos) => [...prevTodos, ...response.documents]);
+ } catch (error) {
+ console.log("Error loading more todos:", error);
+ }
+ setLoading(false);
+ }
+ }
+
+ async function loadNext() {
+ if (todos.length === 0 || (hasMore && !loading)) {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorAfter(lastId),
+ ],
+ );
+ setCurrentPage((prevPage) => prevPage + 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ } catch (error) {
+ console.log("Error loading more todos:", error);
+ }
+ setLoading(false);
+ }
+ }
+
+ async function loadPrev() {
+ if (currentPage > 1) {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorBefore(firstId),
+ ],
+ );
+ setCurrentPage(currentPage - 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ } catch (error) {
+ console.error("Error loading previous todos:", error);
+ }
+ }
+ }
+ async function init() {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(pageLimit)],
+ );
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ setTotalPages(Math.ceil(response.total / pageLimit));
+ } catch (error) {
+ console.log("Error initializing tasks:", error);
+ }
+ setLoading(false);
+ }
+ useEffect(() => {
+ init();
+ }, [pageLimit]);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+In this updated context:
+ * We're using `Query.cursorAfter()` is used to fetch documents that come after a specific document.
+ * We're using `Query.cursorBefore()` is used to fetch documents that come before a specific document.
+ * We've added `lastId` to hold the ID of the last document in the current set of todos.By using this ID with `Query.cursorAfter()`, We're essentially telling Appwrite: "Give me the next set of todos that come after this specific todo item."
+ * We's added `firstId` to hold the ID of the first document in the current set of todos. By using this ID with `Query.cursorBefore()`, we're telling Appwrite: "Give me the set of todos that come before this specific todo item."
+
+Now, you can use this context in your components to display todo items and handle pagination using Appwrite's cursor pagination methods.
+
+# See it in action {% #see-it-in-action %}
+Now's the perfect time to take a moment to run your application and see cursor pagination in action. Navigate using the "Load Previous" and "Load More" buttons, you can manually navigate through tasks, showcasing the efficiency of cursor pagination.This approach ensures a smoother user experience by fetching only a subset of todo items, reducing server load and enhancing responsiveness.
diff --git a/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc
new file mode 100644
index 0000000000..e92c0213b3
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc
@@ -0,0 +1,35 @@
+---
+layout: tutorial
+title: Trade-offs discussion
+description: Learn about the trade-offs between each pagination method in a React app using Appwrite backend.
+step: 9
+---
+# Undstanding pagination with Appwrite and React {% #understanding-pagination-with-appwrite-and-react %}
+Pagination is a crucial feature for applications dealing with large datasets. It allows users to navigate through data in manageable chunks, ensuring that not all data is loaded at once, which can be inefficient and slow. In this section, we'll discuss the trade-offs between different pagination methods when using Appwrite and React.
+# Comparing pagination methods {% #comparing-pagination-methods %}
+
+| Feature / Aspect| Offset Pagination | Cursor Pagination |
+|----------------|-----------------------|-----------------------|
+| Simplicity | High | Medium to Low |
+| Performance | Good for initial pages, degrades with deep pagination |Consistently High |
+| Data Consistency | Can vary with data changes | More stable |
+| Scalability | Good for smaller datasets | Excellent for large datasets |
+| Implementation Complexity | Low | Medium to High |
+| Direct Page Access | Yes | No |
+
+# Choosing the right pagination method {% #choosing-the-right-pagination-method %}
+
+* Offset pagination
+ * Ideal for small datasets or when the dataset doesn't change frequently.
+ * Useful when direct page access is required.
+ * When simplicity and quick Implementation are priorities.
+
+* Cursor pagination
+ * Best suited for large datasets or real-time applications.
+ * Maintains performance even with deep pagination
+ * Ensures more consistent data views, especially with frequently updating datasets.
+
+# Key Takeaways {% #key-takeaways %}
+ * Offset pagination is simple and allow direct page jumps but can suffer from performance issues in deep pagination.
+ * Cursor pagination, while requiring more initial setup, provides consistent performance and is ideal for real-time or large datasets.
+ * When using Appwrite with React, consider the dataset size, update frequency, and user navigation needs when choosing the best pagination method.
\ No newline at end of file