| name | cbpaginator |
|---|---|
| description | Use this skill when implementing server-side pagination in ColdBox/BoxLang using cbpaginator. Covers installation, Paginator service configuration, generating pagination metadata for APIs and views, integrating with QB QueryBuilder, rendering Bootstrap pagination controls, and building pageable collection responses. |
| applyTo | **/*.{bx,cfc,cfm,bxm} |
Load this skill when:
- Adding pagination to a listing handler or REST API endpoint
- Generating page metadata (total, per_page, current_page, last_page, from, to)
- Integrating pagination with QB or qb QueryBuilder
- Building Bootstrap or Tailwind page-number navigation in views
- Creating consistent
{ data: [], pagination: {} }API responses
box install cbpaginatorproperty name="paginator" inject="Pagination@cbpaginator";var page = paginator
.newPagination()
.setTotalRecords( totalCount )
.setMaxRows( rc.perPage ?: 25 )
.setPage( rc.page ?: 1 )
.calculate()
// Available properties after calculate():
page.getPage() // current page number
page.getMaxRows() // per-page count
page.getTotalRecords()
page.getTotalPages()
page.getOffset() // SQL OFFSET value
page.getFrom() // first record on this page
page.getTo() // last record on this page
page.hasPreviousPage()
page.hasNextPage()var page = paginator.paginate(
page = rc.page ?: 1,
maxRows = rc.perPage ?: 25,
totalRecords = userService.count()
)
var users = userService.list(
max = page.getMaxRows(),
offset = page.getOffset()
)function index( event, rc, prc ) {
var page = paginator.paginate(
page = rc.page ?: 1,
maxRows = rc.perPage ?: 25,
totalRecords = userService.count()
)
event.renderData( type = "json", data = {
data : userService.list( max = page.getMaxRows(), offset = page.getOffset() ),
pagination : {
total : page.getTotalRecords(),
per_page : page.getMaxRows(),
current_page : page.getPage(),
last_page : page.getTotalPages(),
from : page.getFrom(),
to : page.getTo()
}
} )
}// Inject QB
property name="qb" inject="QueryBuilder@qb";
property name="paginator" inject="Pagination@cbpaginator";
function index( event, rc, prc ) {
var total = qb.from( "users" ).where( "isActive", true ).count()
var page = paginator.paginate(
page = rc.page ?: 1,
maxRows = 20,
totalRecords = total
)
prc.users = qb.from( "users" )
.where( "isActive", true )
.orderByDesc( "createdAt" )
.limit( page.getMaxRows() )
.offset( page.getOffset() )
.get()
prc.pagination = page
}<cfif prc.pagination.getTotalPages() gt 1>
<nav>
<ul class="pagination">
<!--- Previous --->
<li class="page-item <cfif NOT prc.pagination.hasPreviousPage()>disabled</cfif>">
<a class="page-link"
href="#event.buildLink( 'users.index' )#?page=#prc.pagination.getPage() - 1#">
«
</a>
</li>
<!--- Page numbers --->
<cfloop from="1" to="#prc.pagination.getTotalPages()#" index="i">
<li class="page-item <cfif i eq prc.pagination.getPage()>active</cfif>">
<a class="page-link"
href="#event.buildLink( 'users.index' )#?page=#i#">#i#</a>
</li>
</cfloop>
<!--- Next --->
<li class="page-item <cfif NOT prc.pagination.hasNextPage()>disabled</cfif>">
<a class="page-link"
href="#event.buildLink( 'users.index' )#?page=#prc.pagination.getPage() + 1#">
»
</a>
</li>
</ul>
</nav>
</cfif>- Always validate
rc.pageandrc.perPageare positive integers before passing topaginate() - Cap
maxRowsat a reasonable limit (e.g., 100) to prevent unbounded queries - Run the count query before paginate() — ensure accurate total records
- Return offset from the paginator, not calculated manually, to cover edge-case page-boundary arithmetic
- Clamp page to valid range — if the requested page exceeds totalPages, redirect or return the last page
- Include pagination metadata in every list API response for consistent consumer contracts
- cbpaginator: https://github.com/coldbox-modules/cbpaginator