diff --git a/src/web/hooks/__tests__/usePagination.jsx b/src/web/hooks/__tests__/usePagination.jsx
new file mode 100644
index 0000000000..827bf344c7
--- /dev/null
+++ b/src/web/hooks/__tests__/usePagination.jsx
@@ -0,0 +1,108 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/* eslint-disable react/prop-types */
+
+import {describe, test, expect, testing} from '@gsa/testing';
+
+import {fireEvent, render, screen} from 'web/utils/testing';
+
+import Filter from 'gmp/models/filter';
+
+import usePagination from '../usePagination';
+
+const TestComponent = ({filter, counts, changeFilter}) => {
+ const [first, last, next, previous] = usePagination(
+ filter,
+ counts,
+ changeFilter,
+ );
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+describe('usePageFilter', () => {
+ test('should change the filter for the first page', () => {
+ const filter = Filter.fromString('first=10');
+ const counts = {filtered: 100, rows: 10};
+ const changeFilter = testing.fn();
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId('first'));
+
+ expect(changeFilter).toHaveBeenCalledWith(Filter.fromString('first=1'));
+ });
+
+ test('should change the filter for the last page', () => {
+ const filter = Filter.fromString('first=10');
+ const counts = {filtered: 100, rows: 10};
+ const changeFilter = testing.fn();
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId('last'));
+
+ expect(changeFilter).toHaveBeenCalledWith(Filter.fromString('first=91'));
+ });
+
+ test('should change the filter for the next page', () => {
+ const filter = Filter.fromString('first=10');
+ const counts = {filtered: 100, rows: 10};
+ const changeFilter = testing.fn();
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId('next'));
+
+ expect(changeFilter).toHaveBeenCalledWith(
+ Filter.fromString('first=20 rows=10'),
+ );
+ });
+
+ test('should change the filter for the previous page', () => {
+ const filter = Filter.fromString('first=10');
+ const counts = {filtered: 100, rows: 10};
+ const changeFilter = testing.fn();
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId('previous'));
+
+ expect(changeFilter).toHaveBeenCalledWith(
+ Filter.fromString('first=1 rows=10'),
+ );
+ });
+});
diff --git a/src/web/hooks/usePagination.js b/src/web/hooks/usePagination.js
new file mode 100644
index 0000000000..4149d0567e
--- /dev/null
+++ b/src/web/hooks/usePagination.js
@@ -0,0 +1,54 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {useCallback} from 'react';
+
+/**
+ * Hook to to get the filter for the next, previous, last and first page for a
+ * list of entities
+ *
+ * @example
+ *
+ * const gmp = useGmp();
+ * const [filter, setFilter] = useState(new Filter());
+ * const [entities, setEntities] = useState([]);
+ * const [counts, setCounts] = useState({});
+ * const updateFilter = useCallback(filter => {
+ * setFilter(filter);
+ * gmp.tasks.get(filter).then(response => {setEntities(response.data);setCounts(response.meta.counts)});
+ * }, [gmp.tasks]);
+ * const [first, last, next, previous] = usePagination(filter, counts, updateFilter)
+ *
+ * @param {Filter} filter Current applied filter
+ * @param {Object} counts Current entities counts. Required for calculating the
+ * last page.
+ * @param {Function} changeFilter Function to call when the new filter is applied
+ * @returns {Array} Tuple of functions to update the filter for the first, last,
+ * next and previous page.
+ */
+const usePagination = (filter, counts, changeFilter) => {
+ const getNext = useCallback(() => {
+ changeFilter(filter.next());
+ }, [filter, changeFilter]);
+
+ const getPrevious = useCallback(() => {
+ changeFilter(filter.previous());
+ }, [filter, changeFilter]);
+
+ const getFirst = useCallback(() => {
+ changeFilter(filter.first());
+ }, [filter, changeFilter]);
+
+ const getLast = useCallback(() => {
+ const last =
+ Math.floor((counts.filtered - 1) / counts.rows) * counts.rows + 1;
+ const newFilter = filter.first(last);
+ changeFilter(newFilter);
+ }, [filter, counts, changeFilter]);
+
+ return [getFirst, getLast, getNext, getPrevious];
+};
+
+export default usePagination;