Introduction
Hi friends, this is part 5 where I will explain the usage of the PnP Paging control. We will see how we can leverage this control on our SPFx projects and make use of the feature provided by this control. Thanks to the community.
About the control
This control allows us to render the contents with paging. This control can be used with other controls like Listview to render the contents in paging. There are few properties available with this control to display the paging based on our requirement.
Focus on the Code
You can refer Part 1 post on the creation of the SPFx project and installing all the dependencies. In addition to the dependencies mentioned in that post you have to install @pnp/sp – v3.5.1. You should also do some changes to the project files to make the pnpjs version 3 to work on SPFx 1.14.0. Please follow this link to do the changes.
Note: I have used the ListView webpart and added the PnP Paging control to the same web part. Please refer to the PnP React Controls Part 4.1 – Extending ListView Control to start and it has multiple linked post for extending the Listview control.
Created a file named ExtendListViewPnPPaging.tsx which is a copy of ExtendListViewDemo.tsx
import * as React from 'react';
import { useState, useEffect } from 'react';
import styles from './ListViewDemo.module.scss';
import { IListViewDemoProps } from './IListViewDemoProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp/spfx-controls-react/lib/ListView";
import { useBoolean } from '@uifabric/react-hooks';
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import { Persona, PersonaSize } from 'office-ui-fabric-react/lib/Persona';
import { IColumn } from 'office-ui-fabric-react/lib/DetailsList';
import * as moment from 'moment';
import ListViewContextMenu from './ListViewContextMenu';
import { Pagination } from "@pnp/spfx-controls-react/lib/Pagination";
const slice: any = require('lodash/slice');
const ExtendListViewPnPPaging: React.FC<IListViewDemoProps> = (props) => {
const {
description,
isDarkTheme,
environmentMessage,
hasTeamsContext,
userDisplayName
} = props;
const [loading, { setTrue: displayLoading, setFalse: hideLoading }] = useBoolean(true);
const [listItems, setListItems] = useState<any[]>(undefined);
const [pagedItems, setPagedItems] = useState<any[]>([]);
const [pageSize, setPageSize] = useState<number>(10);
const [currentPage, setCurrentPage] = useState<number>(1);
const viewFields: IViewField[] = [
{
name: 'CaseID',
displayName: 'Case ID',
minWidth: 100,
maxWidth: 150,
sorting: true
},
{
name: '',
displayName: '',
sorting: false,
maxWidth: 40,
render: (rowItem: any) => {
return (
<ListViewContextMenu item={rowItem} />
);
}
},
{
name: 'CaseStatus',
displayName: 'Case Status',
minWidth: 100,
maxWidth: 150
},
{
name: 'BizSegment',
displayName: 'Business Segment',
minWidth: 100,
maxWidth: 150
},
{
name: 'Author.Title',
displayName: 'Created By',
minWidth: 200,
maxWidth: 250,
sorting: true,
render: (item?: any, index?: number, column?: IColumn) => {
return (
<Persona size={PersonaSize.size24} showInitialsUntilImageLoads imageShouldStartVisible
text={item['Author.Title']} imageUrl={`/_layouts/15/userphoto.aspx?username=${item['Author.EMail']}&size=M`} />
);
}
},
{
name: 'Created',
displayName: 'Created',
minWidth: 200,
maxWidth: 250,
sorting: true,
render: (item?: any, index?: number, column?: IColumn) => {
return (
<div>{moment(item.Created).format("DD-MMM-YYYY")}</div>
);
}
}
];
const groupFields: IGrouping[] = [
{
name: 'Author.Title',
order: GroupOrder.ascending
}
];
const _getListItems = async (): Promise<void> => {
const items: any[] = await props.sp.web.lists.getByTitle('Case Master').items
.select('CaseID', 'CaseStatus', 'BizSegment', 'Author/Title', 'Author/EMail', 'Created')
.expand('Author')
();
setListItems(items);
hideLoading();
};
const _getSelectedItem = (items: any[]) => {
console.log('Selected Item(s): ', items);
};
const _onPageUpdate = async (pageno?: number) => {
var currentPge = (pageno) ? pageno : currentPage;
var startItem = ((currentPge - 1) * pageSize);
var endItem = currentPge * pageSize;
let filItems = slice(listItems, startItem, endItem);
setCurrentPage(currentPge);
setPagedItems(filItems);
};
useEffect(() => {
if (listItems) _onPageUpdate();
}, [listItems]);
useEffect(() => { _getListItems() }, []);
return (
<section className={`${styles.listViewDemo} ${hasTeamsContext ? styles.teams : ''}`}>
<div className={styles.welcome}>
<img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
<h2>Well done, {escape(userDisplayName)}!</h2>
</div>
<div>
{loading ? (
<div>Loading, Please wait...</div>
) : (
<>
<ListView
items={pagedItems}
viewFields={viewFields}
compact={true}
selectionMode={SelectionMode.multiple}
selection={_getSelectedItem}
showFilter={true}
filterPlaceHolder="Search..."
stickyHeader={true}
groupByFields={groupFields}
/>
<div style={{ width: '100%', display: 'inline-block' }}>
<Pagination
currentPage={currentPage}
totalPages={Math.round(listItems.length/pageSize)}
onChange={(page) => _onPageUpdate(page)}
limiter={3} // Optional - default value 3
/>
</div>
</>
)}
</div>
</section>
);
};
export default ExtendListViewPnPPaging;
- Added relevant imports to the PnP paging control
- Added the Pagination control and configured the below required properties
- currentPage – The current page number displayed to the users.
- totalPages – Total pages based on the items. Used the formula Math.round(<Total Items Count>/<Items displayed per page>).
- onChange – Callback method to invoke when the the page numbers or arrows are clicked
- limiter – Optional property to display the number of pages to be displayed minimally.
Following are the additional properties apart from the properties used above
- hideFirstPageJump – To hide the button to navigate to the first page
- hideLastPageJump – To hide the button to naviagate to the last page
- limiterIcon – Fluent UI icon to be displayed as a limiter button to show additional pages if any apart from the pages displayed currently.
Reference URL
ListView – @pnp/spfx-controls-react
Sample Implementation

Conclussion
I hope you had learned something about one of the pnp control. There are lot to come in future.
Please free to post your comments and let me know if you want me to post article on any particular feature or plugins that relates to SharePoint. Thanks for your support.
sudharsank/pnpcontrols-demo: Demo project on different PnP React Controls. (github.com)
Happy Coding…