diff --git a/src/routes/docs/tutorials/pagination/+layout.svelte b/src/routes/docs/tutorials/pagination/+layout.svelte
new file mode 100644
index 0000000000..fb9fb3980f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/+layout.svelte
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/routes/docs/tutorials/pagination/+layout.ts b/src/routes/docs/tutorials/pagination/+layout.ts
new file mode 100644
index 0000000000..562b11506f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/+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/+page.ts b/src/routes/docs/tutorials/pagination/+page.ts
new file mode 100644
index 0000000000..1726993fa1
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/+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/step-1');
+};
diff --git a/src/routes/docs/tutorials/pagination/step-1/+page.markdoc b/src/routes/docs/tutorials/pagination/step-1/+page.markdoc
new file mode 100644
index 0000000000..4443ddcc61
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-1/+page.markdoc
@@ -0,0 +1,32 @@
+---
+layout: tutorial
+title: Implementing pagination with Appwrite and React
+description: Dive deep into integrating pagination in a React application using the Appwrite backend
+step: 1
+difficulty: intermediate
+readtime: 20
+---
+
+ 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.
+
+# Why pagination? {% #why-pagination %}
+Pagination is critical in web development, especially when working with huge databases. It helps users explore and process data without feeling overwhelmed by breaking it down into smaller parts. Data-rich applications like search engines and e-commerce sites require this.
+
+The benefits are numerous:
+* faster load times
+* streamlined data management
+* a tidier, more user-friendly interface
+
+# Learning Objectives {% #learning-objectives %}
+By the end of this tutorial, you'll be equipped with:
+1. The knowledge to set up a React project integrated with Appwrite.
+2. Skills to craft queries using offset pagination and understand its implications.
+3. Expertise in constructing queries with cursor pagination and recognizing its benefits.
+4. Insight into the pros and cons of different pagination techniques.
+
+# Prerequisites {% #prerequisites %}
+Before diving in, ensure you:
+
+1. Basic knowledge of JavaScript and React.
+2. Basic understanding of databases or backend systems.
+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/step-10/+page.markdoc b/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
new file mode 100644
index 0000000000..c7648bf6ed
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/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/step-2/+page.markdoc b/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
new file mode 100644
index 0000000000..d3b486bdba
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/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/step-3/+page.markdoc b/src/routes/docs/tutorials/pagination/step-3/+page.markdoc
new file mode 100644
index 0000000000..ec10bfb47e
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-3/+page.markdoc
@@ -0,0 +1,60 @@
+---
+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.
+
+{% info title="One Client() instance per an app" %}
+It's recommended to create only one instance of the `Client()` class for each app.
+{% /info %}
+
+Insert the following code, making sure to replace `` with your actual project ID.
+
+```js
+import { Client, Databases } from "appwrite";
+
+const client = new Client();
+
+client
+ .setEndpoint("https://cloud.appwrite.io/v1")
+ .setProject("[YOUR_PROJECT_ID]"); // Replace with your project ID
+
+export const databases = new Databases(client);
+```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-4/+page.markdoc b/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
new file mode 100644
index 0000000000..42afc2cbc3
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/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
+---
+Databases are essential for dynamic applications, ensuring data is organized and accessible. They form the backbone for data storage and management. In this step, we'll integrate a database into our application using the Appwrite Web SDK, setting the stage for more advanced functionalities.
+
+# 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 to your database, and click **Create collection**. Name the collection "Todos" as it will store our todo items.
+
+When setting up your collection, make sure it has these attributes:
+
+{% 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/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
new file mode 100644
index 0000000000..7d9b06c782
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
@@ -0,0 +1,67 @@
+---
+layout: tutorial
+title: Environment setup
+description: Setup the environment for seeding the database
+step: 5
+---
+# Create environment configuration 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
+APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID]
+APPWRITE_DATABASE_ID=[YOUR_DATABASE_ID]
+APPWRITE_COLLECTION_ID=[YOUR_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.
+
+# 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 { databases } from "../src/lib/appwrite.js";
+import { ID } from "appwrite";
+import dotenv from "dotenv";
+
+dotenv.config();
+
+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(
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.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/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
new file mode 100644
index 0000000000..07c2e378fd
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
@@ -0,0 +1,190 @@
+---
+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 on the page. In React, [context](https://reactjs.org/docs/context.html) allows us to share data across components. We'll leverage context and create two custom hooks to manage our todos. One for each pagination method.
+
+Create a new file named `src/lib/context/todos-offset-pagination.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/todos-cursor-pagination.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 function 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/todos-offset-pagination";
+import { CursorProvider } from "./lib/context/todos-cursor-pagination";
+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:
+
+```js
+import { useTodos } from "../lib/context/todos";
+export function Todos() {
+ const todos = useTodos();
+ return (
+
+
Todos
+
+ {todos.current.map((todo) => (
+
+ {`Task-${todo.taskId}: `}
+
+
+
+ ))}
+
+
+ );
+}
+```
+we will expand these and add the UI next.
+The next steps will involve integrating offset or cursor pagination in our application.
diff --git a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
new file mode 100644
index 0000000000..2db6c4f88e
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
@@ -0,0 +1,267 @@
+---
+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/todos";
+
+function BasicPagination(props) {
+ const { loadPrev, loadNext, hasNext, hasPrev } = props;
+
+ const debounce = (func, delay) => {
+ let debounceTimer;
+ return (...args) => {
+ clearTimeout(debounceTimer);
+ debounceTimer = setTimeout(() => func(...args), delay);
+ };
+ };
+
+ const debouncedLoadPrev = useCallback(debounce(loadPrev, 300), [loadPrev]);
+
+ const debouncedLoadNext = useCallback(debounce(loadNext, 300), [loadNext]);
+
+ return (
+
+ 1}
+ />
+
+ );
+}
+export default TodosWithOffsetPagination;
+```
+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/todos";
+
+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 the todos context {% #update-the-todos-context %}
+
+Update the contents of `src/lib/context/todos.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.APPWRITE_DATABASE_ID,
+ import.meta.env.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/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
new file mode 100644
index 0000000000..c75f43f252
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
@@ -0,0 +1,261 @@
+---
+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/todos-cursor-pagination";
+
+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/todos-cursor-pagination";
+
+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 todos context {% #updating-todos-context %}
+
+Update the contents of `src/lib/context/todos-cursor-pagination.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 TodosProvider2(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.APPWRITE_DATABASE_ID,
+ import.meta.env.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 loadPrev() {
+ if (currentPage > 1) {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.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.APPWRITE_DATABASE_ID,
+ import.meta.env.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/step-9/+page.markdoc b/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
new file mode 100644
index 0000000000..e92c0213b3
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination/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