You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
INSERT INTO blog_posts (title, description, icon, created_at, content)
3
+
VALUES
4
+
(
5
+
'How to use the pagination component',
6
+
'Concrete advice on how to make your SQLPage webapp fast',
7
+
'sailboat-2',
8
+
'2025-11-10',
9
+
'
10
+
# How to use the pagination component
11
+
12
+
To display a large number of records from a database, it is often practical to split these data into pages. The user can thus navigate from one page to another, as well as directly to the first or last page. With SQLPage, it is possible to perform these operations using the pagination component.
13
+
14
+
This component offers many options, and I recommend consulting its documentation before proceeding with the rest of this tutorial.
15
+
16
+
Of course, this component only handles its display and does not implement any logic for data processing or state changes. In this tutorial, we will implement a complete example of using the pagination component with a SQLite database, but the code should work without modification (or with very little modification) with any relational database management system (RDBMS).
17
+
18
+
## Initialization
19
+
20
+
We first need to define two constants that indicate the maximum number of rows per page and the maximum number of pages that the component should display.
21
+
22
+
```
23
+
SET MAX_RECORD_PER_PAGE = 10;
24
+
SET MAX_PAGES = 10;
25
+
```
26
+
27
+
Now, we need to know the number of rows present in the table to be displayed. We can then calculate the number of pages required.
28
+
29
+
```
30
+
SET records_count = (SELECT COUNT(*) FROM album);
31
+
SET pages_count = (CAST($records_count AS INTEGER) / CAST($MAX_RECORD_PER_PAGE AS INTEGER));
32
+
```
33
+
34
+
It is possible that the number of rows in the table is greater than the estimated number of pages multiplied by the number of rows per page. In this case, it is necessary to add an additional page.
35
+
36
+
```
37
+
SET pages_count = (
38
+
CASE
39
+
WHEN (CAST($pages_count AS INTEGER) * CAST($MAX_RECORD_PER_PAGE AS INTEGER)) = CAST($records_count AS INTEGER) THEN $pages_count
40
+
ELSE (CAST($pages_count AS INTEGER) + 1)
41
+
END
42
+
);
43
+
```
44
+
45
+
We will need to transmit the page number to be displayed in the URL using the `page` parameter. We do the same for the number of the first page (`idx_page`) appearing at the left end of the pagination component.
46
+
47
+

48
+
49
+
50
+
If the page number or index is not present in the URL, the value of 1 is applied by default.
51
+
52
+
```
53
+
SET page = COALESCE($page,1);
54
+
SET idx_page = COALESCE($idx_page,1);
55
+
```
56
+
57
+
## Read the data
58
+
59
+
We can now read and display the data based on the active page. To do this, we simply use a table component.
60
+
61
+
```
62
+
SELECT
63
+
''table'' as component
64
+
SELECT
65
+
AlbumId AS id,
66
+
Title AS title
67
+
FROM
68
+
album
69
+
LIMIT CAST($MAX_RECORD_PER_PAGE AS INTEGER)
70
+
OFFSET (CAST($page AS INTEGER) - 1) * CAST($MAX_RECORD_PER_PAGE AS INTEGER);
71
+
```
72
+
73
+
The SQL LIMIT clause allows us to not read more rows than the maximum allowed for a page. With the SQL OFFSET clause, we specify from which row the data is selected.
74
+
75
+
On each HTML page load, the table content will be updated based on the `page` and `idx_page` variables, whose values will be extracted from the URL
76
+
77
+
## Set up the pagination component
78
+
79
+
Now, we need to set up the parameters that will be included in the URL for the buttons to navigate to the previous or next page.
80
+
81
+
If the user wants to view the previous page and the current page is not the first one, the value of the `page` variable is decremented. The same applies to `idx_page`, which is decremented if its value does not correspond to the first page.
82
+
83
+
```
84
+
SET previous_parameters = (
85
+
CASE
86
+
WHEN CAST($page AS INTEGER) > 1 THEN
87
+
json_object(
88
+
''page'', (CAST($page AS INTEGER) - 1),
89
+
''idx_page'', (CASE
90
+
WHEN CAST($idx_page AS INTEGER) > 1 THEN (CAST($idx_page AS INTEGER) - 1)
91
+
ELSE $idx_page
92
+
END)
93
+
)
94
+
ELSE json_object() END
95
+
);
96
+
```
97
+
98
+
The logic is quite similar for the URL to view the next page. First, it is necessary to verify that the user is not already on the last page. Then, the `page` variable can be incremented and the `idx_page` variable updated.
99
+
100
+
```
101
+
SET next_parameters = (
102
+
CASE
103
+
WHEN CAST($page AS INTEGER) < CAST($pages_count AS INTEGER) THEN
104
+
json_object(
105
+
''page'', (CAST($page AS INTEGER) + 1),
106
+
''idx_page'', (CASE
107
+
WHEN CAST($idx_page AS INTEGER) < (CAST($pages_count AS INTEGER) - CAST($MAX_PAGES AS INTEGER) + 1) THEN (CAST($idx_page AS INTEGER) + 1)
108
+
ELSE $idx_page
109
+
END)
110
+
)
111
+
ELSE json_object() END
112
+
);
113
+
```
114
+
115
+
We can now add the pagination component, which is placed below the table displaying the data. All the logic for managing the buttons is entirely handled in SQL:
116
+
- the buttons to access the first or last page,
117
+
- the buttons to view the previous or next page,
118
+
- the enabling or disabling of these buttons based on the context.
119
+
120
+
```
121
+
SELECT
122
+
''pagination'' AS component,
123
+
(CAST($page AS INTEGER) = 1) AS first_disabled,
124
+
(CAST($page AS INTEGER) = 1) AS previous_disabled,
125
+
(CAST($page AS INTEGER) = CAST($pages_count AS INTEGER)) AS next_disabled,
126
+
(CAST($page AS INTEGER) = CAST($pages_count AS INTEGER)) AS last_disabled,
127
+
sqlpage.link(sqlpage.path(), json_object(''page'', 1, ''idx_page'', 1)) as first_link,
128
+
sqlpage.link(sqlpage.path(), $previous_parameters) AS previous_link,
129
+
sqlpage.link(sqlpage.path(), $next_parameters) AS next_link,
WHEN (CAST($pages_count AS INTEGER) <= CAST($MAX_PAGES AS INTEGER)) THEN 1
135
+
ELSE (CAST($pages_count AS INTEGER) - CAST($MAX_PAGES AS INTEGER) + 1)
136
+
END)
137
+
)
138
+
) AS last_link,
139
+
TRUE AS outline;
140
+
```
141
+
142
+
The final step is to generate the page numbers based on the number of pages and the index of the first page displayed to the left of the component. To do this, we use a recursive CTE query.
143
+
144
+
```
145
+
WITH RECURSIVE page_numbers AS (
146
+
SELECT $idx_page AS number
147
+
UNION ALL
148
+
SELECT number + 1
149
+
FROM page_numbers
150
+
LIMIT CAST($MAX_PAGES AS INTEGER)
151
+
)
152
+
SELECT
153
+
number AS contents,
154
+
sqlpage.link(sqlpage.path(), json_object(''page'', number, ''idx_page'', $idx_page)) as link,
155
+
(number = CAST($page AS INTEGER)) AS active
156
+
FROM page_numbers;
157
+
```
158
+
159
+
If the added page matches the content of the `page` variable, the `active` option is set to `TRUE` so that the user knows it is the current page.
0 commit comments