Table

Table displays data in tabular format.


import { TableModule } from 'primeng/table';

DataTable requires a collection to display along with column components for the representation of the data.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{ product.code }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.category }}</td>
            <td>{{ product.quantity }}</td>
        </tr>
    </ng-template>
</p-table>

Columns can be defined dynamically using the *ngFor directive.

Code Name Category Quantity
f230fh0g3 Bamboo Watch Accessories 24
nvklal433 Black Watch Accessories 61
zz21cz3c1 Blue Band Fitness 2
244wgerg2 Blue T-Shirt Clothing 25
h456wer53 Bracelet Accessories 73


<p-table [columns]="cols" [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns">
                {{ col.header }}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns">
        <tr>
            <td *ngFor="let col of columns">
                {{ rowData[col.field] }}
            </td>
        </tr>
    </ng-template>
</p-table>

Custom content at header, body and footer sections are supported via templating.

Products
NameImagePriceCategoryReviewsStatus
Bamboo WatchBamboo Watch$65.00Accessories
INSTOCK
Black WatchBlack Watch$72.00Accessories
INSTOCK
Blue BandBlue Band$79.00Fitness
LOWSTOCK
Blue T-ShirtBlue T-Shirt$29.00Clothing
INSTOCK
BraceletBracelet$15.00Accessories
INSTOCK


<p-table [value]="products" [tableStyle]="{'min-width': '60rem'}">
    <ng-template pTemplate="caption">
        <div class="flex align-items-center justify-content-between">
            Products
            <p-button icon="pi pi-refresh"></p-button>
        </div>
    </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th>Name</th>
            <th>Image</th>
            <th>Price</th>
            <th>Category</th>
            <th>Reviews</th>
            <th>Status</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.name}}</td>
            <td><img [src]="'https://primefaces.org/cdn/primeng/images/demo/product/' + product.image" [alt]="product.name" width="100" class="shadow-4" /></td>
            <td>{{product.price | currency:'USD'}}</td>
            <td>{{product.category}}</td>
            <td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
            <td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
        </tr>
    </ng-template>
    <ng-template pTemplate="summary">
        <div class="flex align-items-center justify-content-between">
            In total there are {{products ? products.length : 0 }} products.
        </div>
    </ng-template>
</p-table>

In addition to a regular table, alternatives with alternative sizes are available.

Small
Normal
Large
CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<div class="flex justify-content-center mb-3">
    <p-selectButton [options]="sizes" [(ngModel)]="selectedSize" [multiple]="false" optionLabel="name" optionValue="class"></p-selectButton>
</div>
<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }" [styleClass]="selectedSize.class">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{ product.code }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.category }}</td>
            <td>{{ product.quantity }}</td>
        </tr>
    </ng-template>
</p-table>

Adding p-datatable-gridlines class displays grid lines.

Header
CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" styleClass="p-datatable-gridlines" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="caption"> Header </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{ product.code }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.category }}</td>
            <td>{{ product.quantity }}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="summary"> Footer </ng-template>
</p-table>

Adding p-datatable-striped class displays striped rows.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" styleClass="p-datatable-striped" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Certain rows or cells can easily be styled based on conditions.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories
24
nvklal433Black WatchAccessories
61
zz21cz3c1Blue BandFitness
2
244wgerg2Blue T-ShirtClothing
25
h456wer53BraceletAccessories
73


<p-table [value]="products" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr [ngClass]="{'row-accessories': product.category === 'Accessories'}">
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>
                <div [ngClass]="{'outofstock': product.quantity === 0, 'lowstock': (product.quantity > 0 && product.quantity < 10),'instock': product.quantity > 10}">
                    {{product.quantity}}
                </div>
            </td>
        </tr>
    </ng-template>
</p-table>

When there is not enough space for the table to fit all the content efficiently, table displays a horizontal scrollbar. It is suggested to give a min-width to the table to avoid design issues due wrapping of cell contents.

Following table displays a horizontal scrollbar when viewport is smaller than 50rem.

NamePriceCategoryQuantityStatusReviews
Bamboo Watch$65.00Accessories24INSTOCK
Black Watch$72.00Accessories61INSTOCK
Blue Band$79.00Fitness2LOWSTOCK
Blue T-Shirt$29.00Clothing25INSTOCK
Bracelet$15.00Accessories73INSTOCK


<p-table [value]="products" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th>Name </th>
            <th>Price</th>
            <th>Category</th>
            <th>Quantity</th>
            <th>Status</th>
            <th>Reviews</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-columns="columns">
        <tr>
            <td>{{product.name}}</td>
            <td>{{product.price | currency:'USD'}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
            <td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
            <td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
        </tr>
    </ng-template>
</p-table>

In stack layout, columns are displayed as stacked after a certain breakpoint. Default is '960px' as max-width. This feature is enabled by setting responsiveLayout to stack and adding an element with p-column-title style class to the body cells.

NamePriceCategoryQuantityStatusReviews
NameBamboo WatchPrice$65.00CategoryAccessoriesQuantity24INSTOCKReviews
NameBlack WatchPrice$72.00CategoryAccessoriesQuantity61INSTOCKReviews
NameBlue BandPrice$79.00CategoryFitnessQuantity2LOWSTOCKReviews
NameBlue T-ShirtPrice$29.00CategoryClothingQuantity25INSTOCKReviews
NameBraceletPrice$15.00CategoryAccessoriesQuantity73INSTOCKReviews


<p-table [value]="products" responsiveLayout="stack" [breakpoint]="'960px'" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th>Name</th>
            <th>Price</th>
            <th>Category</th>
            <th>Quantity</th>
            <th>Status</th>
            <th>Reviews</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-columns="columns">
        <tr>
            <td><span class="p-column-title">Name</span>{{product.name}}</td>
            <td><span class="p-column-title">Price</span>{{product.price | currency:'USD'}}</td>
            <td><span class="p-column-title">Category</span>{{product.category}}</td>
            <td><span class="p-column-title">Quantity</span>{{product.quantity}}</td>
            <td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
            <td><span class="p-column-title">Reviews</span><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
        </tr>
    </ng-template>
</p-table>

Pagination is enabled by setting paginator property to true and defining a rows property to specify the number of rows per page. For server side pagination, see the lazy loading example.

NameCountryCompanyRepresentative
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Showing 1 to 10 of 200 entries
10


<p-table
    [value]="customers"
    [paginator]="true"
    [rows]="5"
    [showCurrentPageReport]="true"
    [tableStyle]="{ 'min-width': '50rem' }"
    currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
    [rowsPerPageOptions]="[10, 25, 50]"
>
    <ng-template pTemplate="header">
        <tr>
            <th style="width:25%">Name</th>
            <th style="width:25%">Country</th>
            <th style="width:25%">Company</th>
            <th style="width:25%">Representative</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>{{ customer.name }}</td>
            <td>{{ customer.country.name }}</td>
            <td>{{ customer.company }}</td>
            <td>{{ customer.representative.name }}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="paginatorleft">
        <p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
    </ng-template>
    <ng-template pTemplate="paginatorright">
        <p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
    </ng-template>
</p-table>

Paginator can also be controlled via model using a binding to the first property where changes trigger a pagination.

NameCountryCompanyRepresentative
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Simona MorascaEgyptChapman, Ross E EsqIvan Magalhaes
Mitsue TollnerParaguayMorlong AssociatesIvan Magalhaes
Leota DilliardSerbiaCommercial PressOnyama Limba
Sage WieserEgyptTruhlar And Truhlar AttysIvan Magalhaes
Kris MarrierMexicoKing, Christopher A EsqOnyama Limba
Showing 1 to 10 of 200 entries
10


<p-table
    [value]="customers"
    [paginator]="true"
    [rows]="5"
    [showCurrentPageReport]="true"
    [tableStyle]="{ 'min-width': '50rem' }"
    currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
    [rowsPerPageOptions]="[10, 25, 50]"
>
    <ng-template pTemplate="header">
        <tr>
            <th style="width:25%">Name</th>
            <th style="width:25%">Country</th>
            <th style="width:25%">Company</th>
            <th style="width:25%">Representative</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>{{ customer.name }}</td>
            <td>{{ customer.country.name }}</td>
            <td>{{ customer.company }}</td>
            <td>{{ customer.representative.name }}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="paginatorleft">
        <p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
    </ng-template>
    <ng-template pTemplate="paginatorright">
        <p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
    </ng-template>
</p-table>

A column can be made sortable by adding the pSortableColumn directive whose value is the field to sort against and a sort indicator via p-sortIcon component. For dynamic columns, setting pSortableColumnDisabled property as true disables sorting for that particular column.

Default sorting is executed on a single column, in order to enable multiple field sorting, set sortMode property to "multiple" and use metakey when clicking on another column.

Code Name Category Quantity Price
f230fh0g3Bamboo WatchAccessories24$65.00
nvklal433Black WatchAccessories61$72.00
zz21cz3c1Blue BandFitness2$79.00
244wgerg2Blue T-ShirtClothing25$29.00
h456wer53BraceletAccessories73$15.00


<p-table [value]="products" [tableStyle]="{'min-width': '60rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th pSortableColumn="code" style="width:20%">Code <p-sortIcon field="code"></p-sortIcon></th>
            <th pSortableColumn="name" style="width:20%">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th pSortableColumn="category" style="width:20%">Category <p-sortIcon field="category"></p-sortIcon></th>
            <th pSortableColumn="quantity" style="width:20%">Quantity <p-sortIcon field="quantity"></p-sortIcon></th>
            <th pSortableColumn="price" style="width:20%">Price <p-sortIcon field="price"></p-sortIcon></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
            <td>{{product.price | currency: 'USD'}}</td>
        </tr>
    </ng-template>
</p-table>

Instead of using the built-in sorting algorithm a custom sort can be attached by enabling customSort property and defining a sortFunction implementation. This function gets a SortEvent instance that provides the data to sort, sortField, sortOrder and multiSortMeta.

Code Name Category Quantity Price
f230fh0g3Bamboo WatchAccessories24$65.00
nvklal433Black WatchAccessories61$72.00
zz21cz3c1Blue BandFitness2$79.00
244wgerg2Blue T-ShirtClothing25$29.00
h456wer53BraceletAccessories73$15.00


<p-table [value]="products" (sortFunction)="customSort($event)" [customSort]="true" [tableStyle]="{'min-width': '60rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th pSortableColumn="code" style="width:20%">Code <p-sortIcon field="code"></p-sortIcon></th>
            <th pSortableColumn="name" style="width:20%">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th pSortableColumn="category" style="width:20%">Category <p-sortIcon field="category"></p-sortIcon></th>
            <th pSortableColumn="quantity" style="width:20%">Quantity <p-sortIcon field="quantity"></p-sortIcon></th>
            <th pSortableColumn="price" style="width:20%">Price <p-sortIcon field="price"></p-sortIcon></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
            <td>{{product.price | currency: 'USD'}}</td>
        </tr>
    </ng-template>
</p-table>

Filters are displayed in an overlay.

Name
Country
Agent
Date
Balance
Status
Activity
Verified
James Butt AlgeriaIoni BowcherIoni Bowcher 09/13/2015 $70,663.00 unqualified
Josephine Darakjy EgyptAmy ElsnerAmy Elsner 02/09/2019 $82,429.00 proposal
Art Venere PanamaAsiya JavayantAsiya Javayant 05/13/2017 $28,334.00 qualified
Lenna Paprocki SloveniaXuxue FengXuxue Feng 09/15/2020 $88,521.00 new
Donette Foller South AfricaAsiya JavayantAsiya Javayant 05/20/2016 $93,905.00 proposal
Simona Morasca EgyptIvan MagalhaesIvan Magalhaes 02/16/2018 $50,041.00 qualified
Mitsue Tollner ParaguayIvan MagalhaesIvan Magalhaes 02/19/2018 $58,706.00 renewal
Leota Dilliard SerbiaOnyama LimbaOnyama Limba 08/13/2019 $26,640.00 renewal
Sage Wieser EgyptIvan MagalhaesIvan Magalhaes 11/21/2018 $65,369.00 unqualified
Kris Marrier MexicoOnyama LimbaOnyama Limba 07/07/2015 $63,451.00 proposal
Showing 1 to 10 of 200 entries
10


<p-table
    #dt1
    [value]="customers"
    dataKey="id"
    [rows]="10"
    [showCurrentPageReport]="true"
    [rowsPerPageOptions]="[10, 25, 50]"
    [loading]="loading"
    [paginator]="true"
    currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
    [globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
>
    <ng-template pTemplate="caption">
        <div class="flex">
            <button pButton label="Clear" class="p-button-outlined" icon="pi pi-filter-slash" (click)="clear(dt1)"></button>
            <span class="p-input-icon-left ml-auto">
                <i class="pi pi-search"></i>
                <input pInputText type="text" (input)="dt1.filterGlobal($event.target.value, 'contains')" placeholder="Search keyword" />
            </span>
        </div>
    </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th style="min-width:15rem">
                <div class="flex align-items-center">
                    Name
                    <p-columnFilter type="text" field="name" display="menu"></p-columnFilter>
                </div>
            </th>
            <th style="min-width:15rem">
                <div class="flex align-items-center">
                    Country
                    <p-columnFilter type="text" field="country.name" display="menu"></p-columnFilter>
                </div>
            </th>
            <th style="min-width:15rem">
                <div class="flex align-items-center">
                    Agent
                    <p-columnFilter field="representative" matchMode="in" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
                        <ng-template pTemplate="header">
                            <div class="px-3 pt-3 pb-0">
                                <span class="font-bold">Agent Picker</span>
                            </div>
                        </ng-template>
                        <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                            <p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name">
                                <ng-template let-option pTemplate="item">
                                    <div class="inline-block vertical-align-middle">
                                        <img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
                                        <span class="ml-1 mt-1">{{ option.name }}</span>
                                    </div>
                                </ng-template>
                            </p-multiSelect>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th style="min-width:10rem">
                <div class="flex align-items-center">
                    Date
                    <p-columnFilter type="date" field="date" display="menu"></p-columnFilter>
                </div>
            </th>
            <th style="min-width:10rem">
                <div class="flex align-items-center">
                    Balance
                    <p-columnFilter type="numeric" field="balance" display="menu" currency="USD"></p-columnFilter>
                </div>
            </th>
            <th style="min-width:10rem">
                <div class="flex align-items-center">
                    Status
                    <p-columnFilter field="status" matchMode="equals" display="menu">
                        <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                            <p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any">
                                <ng-template let-option pTemplate="item">
                                    <p-tag [value]="option.value" [severity]="getSeverity(option.label)"></p-tag>
                                </ng-template>
                            </p-dropdown>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th style="min-width:10rem">
                <div class="flex align-items-center">
                    Activity
                    <p-columnFilter field="activity" matchMode="between" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
                        <ng-template pTemplate="filter" let-filter="filterCallback">
                            <p-slider [ngModel]="activityValues" [range]="true" (onSlideEnd)="filter($event.values)" styleClass="m-3"></p-slider>
                            <div class="flex align-items-center px-2">
                                <span>{{ activityValues[0] }}</span>
                                <span>{{ activityValues[1] }}</span>
                            </div>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th style="width: 3rem">
                <div class="flex align-items-center">
                    Verified
                    <p-columnFilter type="boolean" field="verified" display="menu"></p-columnFilter>
                </div>
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>
                {{ customer.name }}
            </td>
            <td>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
                <span class="ml-1 vertical-align-middle">{{ customer.country.name }}</span>
            </td>
            <td>
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
                <span class="ml-1 vertical-align-middle">{{ customer.representative.name }}</span>
            </td>
            <td>
                {{ customer.date | date: 'MM/dd/yyyy' }}
            </td>
            <td>
                {{ customer.balance | currency: 'USD':'symbol' }}
            </td>
            <td>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                <p-progressBar [value]="customer.activity" [showValue]="false"></p-progressBar>
            </td>
            <td class="text-center">
                <i class="pi" [ngClass]="{ 'text-green-500 pi-check-circle': customer.verified, 'text-red-500 pi-times-circle': !customer.verified }"></i>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="emptymessage">
        <tr>
            <td colspan="7">No customers found.</td>
        </tr>
    </ng-template>
</p-table>

Filters are displayed inline within a separate row.

NameCountryAgentStatusVerified
Any
Any
James Butt AlgeriaIoni BowcherIoni Bowcherunqualified
Josephine Darakjy EgyptAmy ElsnerAmy Elsnerproposal
Art Venere PanamaAsiya JavayantAsiya Javayantqualified
Lenna Paprocki SloveniaXuxue FengXuxue Fengnew
Donette Foller South AfricaAsiya JavayantAsiya Javayantproposal
Simona Morasca EgyptIvan MagalhaesIvan Magalhaesqualified
Mitsue Tollner ParaguayIvan MagalhaesIvan Magalhaesrenewal
Leota Dilliard SerbiaOnyama LimbaOnyama Limbarenewal
Sage Wieser EgyptIvan MagalhaesIvan Magalhaesunqualified
Kris Marrier MexicoOnyama LimbaOnyama Limbaproposal
Showing 1 to 10 of 200 entries
10


<p-table
    #dt2
    [value]="customers"
    dataKey="id"
    [rows]="10"
    [showCurrentPageReport]="true"
    [rowsPerPageOptions]="[10, 25, 50]"
    [loading]="loading"
    [paginator]="true"
    currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
    [globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
    [tableStyle]="{ 'min-width': '75rem' }"
    >
    <ng-template pTemplate="caption">
        <div class="flex">
            <span class="p-input-icon-left ml-auto">
                <i class="pi pi-search"></i>
                <input pInputText type="text" (input)="dt2.filterGlobal($event.target.value, 'contains')" placeholder="Search keyword" />
            </span>
        </div>
    </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th style="width:22%">Name</th>
            <th style="width:22%">Country</th>
            <th style="width:22%">Agent</th>
            <th style="width:22%">Status</th>
            <th style="width:12%">Verified</th>
        </tr>
        <tr>
            <th>
                <p-columnFilter type="text" field="name"></p-columnFilter>
            </th>
            <th>
                <p-columnFilter type="text" field="country.name"></p-columnFilter>
            </th>
            <th>
                <p-columnFilter field="representative" matchMode="in" [showMenu]="false">
                    <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                        <p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name">
                            <ng-template let-option pTemplate="item">
                                <div class="inline-block vertical-align-middle">
                                    <img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
                                    <span class="ml-1 mt-1">{{ option.name }}</span>
                                </div>
                            </ng-template>
                        </p-multiSelect>
                    </ng-template>
                </p-columnFilter>
            </th>
            <th>
                <p-columnFilter field="status" matchMode="equals" [showMenu]="false">
                    <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                        <p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any" [showClear]="true">
                            <ng-template let-option pTemplate="item">
                                <p-tag [value]="option.value" [severity]="getSeverity(option.label)"></p-tag>
                            </ng-template>
                        </p-dropdown>
                    </ng-template>
                </p-columnFilter>
            </th>
            <th>
                <p-columnFilter type="boolean" field="verified"></p-columnFilter>
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>
                {{ customer.name }}
            </td>
            <td>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
                <span class="ml-1 vertical-align-middle">{{ customer.country.name }}</span>
            </td>
            <td>
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
                <span class="ml-1 vertical-align-middle">{{ customer.representative.name }}</span>
            </td>
            <td>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                <i class="pi" [ngClass]="{ 'text-green-500 pi-check-circle': customer.verified, 'text-red-500 pi-times-circle': !customer.verified }"></i>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="emptymessage">
        <tr>
            <td colspan="5">No customers found.</td>
        </tr>
    </ng-template>
</p-table>

Single row selection is enabled by defining selectionMode as single along with a value binding using selection property. When available, it is suggested to provide a unique identifier of a row with dataKey to optimize performance.

By default, metaKey press (e.g. ⌘) is necessary to unselect a row however this can be configured with disabling the metaKeySelection property. In touch enabled devices this option has no effect and behavior is same as setting it to false.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" selectionMode="single" [(selection)]="selectedProduct" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr [pSelectableRow]="product">
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

In multiple mode, selection binding should be an array. For touch enabled devices, selection is managed by tapping and for other devices metakey or shiftkey are required. Setting metaKeySelection property as false enables multiple selection without meta key.

MetaKey
Multiple Selection with MetaKey
CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<div class="flex justify-content-center align-items-center gap-2 mb-3">
    <p-inputSwitch inputId="metakey" [(ngModel)]="metaKeySelection" label="MetaKey"></p-inputSwitch>
    <span>MetaKey</span>
</div>
<p-table [value]="products" selectionMode="multiple" [(selection)]="selectedProducts" [metaKeySelection]="metaKeySelection" dataKey="code" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="caption"> Multiple Selection with MetaKey </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-rowIndex="rowIndex">
        <tr [pSelectableRow]="product" [pSelectableRowIndex]="rowIndex">
            <td>{{ product.code }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.category }}</td>
            <td>{{ product.quantity }}</td>
        </tr>
    </ng-template>
</p-table>

Multiple selection can also be handled using checkboxes by enabling the selectionMode property of column as multiple.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem">
                <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
            </th>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>
                <p-tableCheckbox [value]="product"></p-tableCheckbox>
            </td>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Single selection can also be handled using radio buttons.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [(selection)]="selectedProduct" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem"></th>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>
                <p-tableRadioButton [value]="product"></p-tableRadioButton>
            </td>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Row selection can be controlled by utilizing rowSelectable and disabled properties.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [rowSelectable]="isRowSelectable" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem">
                <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
            </th>
            <th style="min-width:200px">Code</th>
            <th style="min-width:200px">Name</th>
            <th style="min-width:200px">Category</th>
            <th style="min-width:200px">Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>
                <p-tableCheckbox [value]="product" [disabled]="isOutOfStock(product)"></p-tableCheckbox>
            </td>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [paginator]="true" [rows]="5" [selectionPageOnly]="true" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem">
                <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
            </th>
            <th style="min-width:200px">Code</th>
            <th style="min-width:200px">Name</th>
            <th style="min-width:200px">Category</th>
            <th style="min-width:200px">Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>
                <p-tableCheckbox [value]="product"></p-tableCheckbox>
            </td>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Table provides onRowSelect and onRowUnselect events to listen selection events.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-toast></p-toast>
<p-table [value]="products" selectionMode="single" [(selection)]="selectedProduct" dataKey="code"
    (onRowSelect)="onRowSelect($event)" (onRowUnselect)="onRowUnselect($event)" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr [pSelectableRow]="product">
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Selection using custom elements.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-toast></p-toast>
<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Quantity</th>
            <th style="width: 5rem"></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{ product.code }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.category }}</td>
            <td>{{ product.quantity }}</td>
            <td>
                <button type="button" pButton pRipple icon="pi pi-plus" (click)="selectProduct(product)"></button>
            </td>
        </tr>
    </ng-template>
</p-table>

Row expansion allows displaying detailed content for a particular row. To use this feature, add a template named rowexpansion and use the pRowToggler directive whose value is the row data instance on an element of your choice whose click event toggles the expansion. This enables providing your custom UI such as buttons, links and so on. Example below uses an anchor with an icon as a toggler. Setting pRowTogglerDisabled as true disables the toggle event for the element.

Name ImagePrice Category Reviews Status
Bamboo WatchBamboo Watch$65.00Accessories
INSTOCK
Black WatchBlack Watch$72.00Accessories
INSTOCK
Blue BandBlue Band$79.00Fitness
LOWSTOCK
Blue T-ShirtBlue T-Shirt$29.00Clothing
INSTOCK
BraceletBracelet$15.00Accessories
INSTOCK
Brown PurseBrown Purse$120.00Accessories
OUTOFSTOCK
Chakra BraceletChakra Bracelet$32.00Accessories
LOWSTOCK
Galaxy EarringsGalaxy Earrings$34.00Accessories
INSTOCK
Game ControllerGame Controller$99.00Electronics
LOWSTOCK
Gaming SetGaming Set$299.00Electronics
INSTOCK


<p-table [value]="products" dataKey="name" [tableStyle]="{ 'min-width': '60rem' }">
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 5rem"></th>
            <th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th>Image</th>
            <th pSortableColumn="price">Price <p-sortIcon field="price"></p-sortIcon></th>
            <th pSortableColumn="category">Category <p-sortIcon field="category"></p-sortIcon></th>
            <th pSortableColumn="rating">Reviews <p-sortIcon field="rating"></p-sortIcon></th>
            <th pSortableColumn="inventoryStatus">Status <p-sortIcon field="inventoryStatus"></p-sortIcon></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-expanded="expanded">
        <tr>
            <td>
                <button type="button" pButton pRipple [pRowToggler]="product" class="p-button-text p-button-rounded p-button-plain" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
            </td>
            <td>{{ product.name }}</td>
            <td><img [src]="'https://primefaces.org/cdn/primeng/images/demo/product/' + product.image" [alt]="product.name" width="50" class="shadow-4" /></td>
            <td>{{ product.price | currency: 'USD' }}</td>
            <td>{{ product.category }}</td>
            <td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
            <td>
                <p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="rowexpansion" let-product>
        <tr>
            <td colspan="7">
                <div class="p-3">
                    <p-table [value]="product.orders" dataKey="id">
                        <ng-template pTemplate="header">
                            <tr>
                                <th pSortableColumn="id">Id <p-sortIcon field="price"></p-sortIcon></th>
                                <th pSortableColumn="customer">Customer <p-sortIcon field="customer"></p-sortIcon></th>
                                <th pSortableColumn="date">Date <p-sortIcon field="date"></p-sortIcon></th>
                                <th pSortableColumn="amount">Amount <p-sortIcon field="amount"></p-sortIcon></th>
                                <th pSortableColumn="status">Status <p-sortIcon field="status"></p-sortIcon></th>
                                <th style="width: 4rem"></th>
                            </tr>
                        </ng-template>
                        <ng-template pTemplate="body" let-order>
                            <tr>
                                <td>{{ order.id }}</td>
                                <td>{{ order.customer }}</td>
                                <td>{{ order.id }}</td>
                                <td>{{ order.amount | currency: 'USD' }}</td>
                                <td>
                                    <p-tag [value]="order.status" [severity]="getStatusSeverity(order.status)"></p-tag>
                                </td>
                                <td><p-button type="button" icon="pi pi-plus"></p-button></td>
                            </tr>
                        </ng-template>
                        <ng-template pTemplate="emptymessage">
                            <tr>
                                <td colspan="6">There are no order for this product yet.</td>
                            </tr>
                        </ng-template>
                    </p-table>
                </div>
            </td>
        </tr>
    </ng-template>
</p-table>

Row editing toggles the visibility of all the editors in the row at once and provides additional options to save and cancel editing. Row editing functionality is enabled by setting the editMode to "row" on table, defining a dataKey to uniquely identify a row, adding pEditableRow directive to the editable rows and defining the UI Controls with pInitEditableRow, pSaveEditableRow and pCancelEditableRow directives respectively.

Save and Cancel functionality implementation is left to the page author to provide more control over the editing business logic. Example below utilizes a simple implementation where a row is cloned when editing is initialized and is saved or restored depending on the result of the editing. An implicit variable called "editing" is passed to the body template so you may come up with your own UI controls that implement editing based on your own requirements such as adding validations and styling. Note that pSaveEditableRow only switches the row to back view mode when there are no validation errors.

Moreover, you may use setting pEditableRowDisabled property as true to disable editing for that particular row and in case you need to display rows in edit mode by default, use the editingRowKeys property which is a map whose key is the dataKey of the record where the value is any arbitrary number greater than zero.

CodeNameInventory StatusPrice
f230fh0g3 Bamboo Watch INSTOCK $65.00
nvklal433 Black Watch INSTOCK $72.00
zz21cz3c1 Blue Band LOWSTOCK $79.00
244wgerg2 Blue T-Shirt INSTOCK $29.00
h456wer53 Bracelet INSTOCK $15.00


<p-toast></p-toast>
<p-table [value]="products" dataKey="id" editMode="row" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width:20%">Code</th>
            <th style="width:20%">Name</th>
            <th style="width:20%">Inventory Status</th>
            <th style="width:20%">Price</th>
            <th style="width:20%"></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-editing="editing" let-ri="rowIndex">
        <tr [pEditableRow]="product">
            <td>
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.code">
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{product.code}}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td>
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.name" required>
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{product.name}}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td>
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <p-dropdown [options]="statuses" appendTo="body" [(ngModel)]="product.inventoryStatus" [style]="{'width':'100%'}"></p-dropdown>
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{product.inventoryStatus}}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td>
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.price">
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{product.price | currency: 'USD'}}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td>
                <div class="flex align-items-center justify-content-center gap-2">
                    <button *ngIf="!editing" pButton pRipple type="button" pInitEditableRow icon="pi pi-pencil" (click)="onRowEditInit(product)" class="p-button-rounded p-button-text"></button>
                    <button *ngIf="editing" pButton pRipple type="button" pSaveEditableRow icon="pi pi-check" (click)="onRowEditSave(product)" class="p-button-rounded p-button-text p-button-success mr-2"></button>
                    <button *ngIf="editing" pButton pRipple type="button" pCancelEditableRow icon="pi pi-times" (click)="onRowEditCancel(product, ri)" class="p-button-rounded p-button-text p-button-danger"></button>
                </div>
            </td>
        </tr>
    </ng-template>
</p-table>

In-cell editing is enabled by adding pEditableColumn directive to an editable cell that has a p-cellEditor helper component to define the input-output templates for the edit and view modes respectively.

CodeNameInventory StatusPrice
f230fh0g3 Bamboo Watch INSTOCK $65.00
nvklal433 Black Watch INSTOCK $72.00
zz21cz3c1 Blue Band LOWSTOCK $79.00
244wgerg2 Blue T-Shirt INSTOCK $29.00
h456wer53 Bracelet INSTOCK $15.00


<p-table [value]="products" dataKey="id" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="header">
        <tr>
            <th style="width:25%">Code</th>
            <th style="width:25%">Name</th>
            <th style="width:25%">Inventory Status</th>
            <th style="width:25%">Price</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-editing="editing">
        <tr>
            <td [pEditableColumn]="product.code" pEditableColumnField="code">
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.code" />
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{ product.code }}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td [pEditableColumn]="product.name" pEditableColumnField="name">
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.name" required />
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{ product.name }}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td [pEditableColumn]="product.inventoryStatus" pEditableColumnField="inventoryStatus">
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText [(ngModel)]="product.inventoryStatus" />
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{ product.inventoryStatus }}
                    </ng-template>
                </p-cellEditor>
            </td>
            <td [pEditableColumn]="product.price" pEditableColumnField="price">
                <p-cellEditor>
                    <ng-template pTemplate="input">
                        <input pInputText type="text" [(ngModel)]="product.price" />
                    </ng-template>
                    <ng-template pTemplate="output">
                        {{ product.price | currency: 'USD' }}
                    </ng-template>
                </p-cellEditor>
            </td>
        </tr>
    </ng-template>
</p-table>

Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking onLazyLoad callback everytime paging, sorting and filtering happens. Sample here loads the data from remote datasource efficiently using lazy loading. Also, the implementation of checkbox selection in lazy tables is left entirely to the user. Since the table component does not know what will happen to the data on the next page or whether there are instant data changes, the selection array can be implemented in several ways. One of them is as in the example below.

Name Country Company Representative
Any
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Simona MorascaEgyptChapman, Ross E EsqIvan Magalhaes
Mitsue TollnerParaguayMorlong AssociatesIvan Magalhaes
Leota DilliardSerbiaCommercial PressOnyama Limba
Sage WieserEgyptTruhlar And Truhlar AttysIvan Magalhaes
Kris MarrierMexicoKing, Christopher A EsqOnyama Limba


<p-table
    [value]="customers"
    [lazy]="true"
    (onLazyLoad)="loadCustomers($event)"
    dataKey="id"
    [tableStyle]="{ 'min-width': '75rem' }"
    [selection]="selectedCustomers"
    (selectionChange)="onSelectionChange($event)"
    [selectAll]="selectAll"
    (selectAllChange)="onSelectAllChange($event)"
    [paginator]="true"
    [rows]="10"
    [totalRecords]="totalRecords"
    [loading]="loading"
    [globalFilterFields]="['name', 'country.name', 'company', 'representative.name']"
>
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem"></th>
            <th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th pSortableColumn="country.name">Country <p-sortIcon field="country.name"></p-sortIcon></th>
            <th pSortableColumn="company">Company <p-sortIcon field="company"></p-sortIcon></th>
            <th pSortableColumn="representative.name">Representative <p-sortIcon field="representative.name"></p-sortIcon></th>
        </tr>
        <tr>
            <th style="width: 4rem">
                <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
            </th>
            <th>
                <p-columnFilter type="text" field="name"></p-columnFilter>
            </th>
            <th>
                <p-columnFilter type="text" field="country.name"></p-columnFilter>
            </th>
            <th>
                <p-columnFilter type="text" field="company"></p-columnFilter>
            </th>
            <th>
                <p-columnFilter field="representative" matchMode="in" [showMenu]="false">
                    <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                        <p-multiSelect [ngModel]="value" appendTo="body" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name" [maxSelectedLabels]="1" [selectedItemsLabel]="'{0} items'">
                            <ng-template let-option pTemplate="item">
                                <div class="inline-block vertical-align-middle">
                                    <img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
                                    <span class="ml-1 mt-1">{{ option.name }}</span>
                                </div>
                            </ng-template>
                        </p-multiSelect>
                    </ng-template>
                </p-columnFilter>
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>
                <p-tableCheckbox [value]="customer"></p-tableCheckbox>
            </td>
            <td>{{ customer.name }}</td>
            <td>{{ customer.country.name }}</td>
            <td>{{ customer.company }}</td>
            <td>{{ customer.representative.name }}</td>
        </tr>
    </ng-template>
</p-table>

Adding scrollable property along with a scrollHeight for the data viewport enables vertical scrolling with fixed headers.

NameCountryCompanyRepresentative
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Simona MorascaEgyptChapman, Ross E EsqIvan Magalhaes
Mitsue TollnerParaguayMorlong AssociatesIvan Magalhaes
Leota DilliardSerbiaCommercial PressOnyama Limba
Sage WieserEgyptTruhlar And Truhlar AttysIvan Magalhaes
Kris MarrierMexicoKing, Christopher A EsqOnyama Limba
Minna AmigonRomaniaDorl, James J EsqAnna Fali
Abel MacleadSingaporeRangoni Of FlorenceBernardo Dominic
Kiley CaldareraSerbiaFeiner BrosOnyama Limba
Graciela RutaChileBuckley Miller & WrightAmy Elsner
Cammy AlbaresPhilippinesRousseaux, Michael EsqAsiya Javayant
Mattie PoquetteVenezuelaCentury CommunicationsAnna Fali
Meaghan GarufiMalaysiaBolton, Wilbur EsqIvan Magalhaes
Gladys RimNetherlandsT M Byxbee Company PcStephen Shaw
Yuki WhobreyIsraelFarmers Insurance GroupBernardo Dominic
Fletcher FlosiArgentinaPost Box Services PlusXuxue Feng
Bette NickaParaguaySport En ArtOnyama Limba
Veronika InouyeEcuadorC 4 Network IncIoni Bowcher
Willard KolmetzTunisiaIngalls, Donald R EsqAsiya Javayant
Maryann RoysterBelarusFranklin, Peter L EsqElwin Sharvill
Alisha SlusarskiIcelandWtlz Power 107 FmStephen Shaw
Allene IturbideItalyLedecky, David EsqIvan Magalhaes
Chanel CaudyArgentinaProfessional Image IncIoni Bowcher
Ezekiel ChuiIrelandSider, Donald C EsqAmy Elsner
Willow KuskoRomaniaU Pull ItOnyama Limba
Bernardo FigeroaIsraelClark, Richard CpaIoni Bowcher
Ammie CorrioHungaryMoskowitz, Barry SAsiya Javayant
Francine VocelkaHondurasCascade Realty Advisors IncIoni Bowcher
Ernie StensethAustraliaKnwz NewsradioXuxue Feng
Albina GlickUkraineGiampetro, Anthony DBernardo Dominic
Alishia SergiQatarMilford Enterprises IncIvan Magalhaes
Solange ShinkoCameroonMosocco, Ronald AOnyama Limba
Jose StockhamItalyTri State Refueler CoAmy Elsner
Rozella OstroskyVenezuelaParkway CompanyAmy Elsner
Valentine GillianParaguayFbs Business FinanceBernardo Dominic
Kati RulapaughPuerto RicoEder Assocs Consltng Engrs PcIoni Bowcher
Youlanda SchemmerBoliviaTri M Tool IncXuxue Feng
Dyan OldroydArgentinaInternational Eyelets IncAmy Elsner
Roxane CampainFranceRapid Trading IntlAnna Fali
Lavera PerinVietnamAbc Enterprises IncStephen Shaw
Erick FerenczBelgiumCindy Turner AssociatesAmy Elsner
Fatima SaylorsCanadaStanton, James D EsqOnyama Limba
Jina BriddickMexicoGrace Pastries IncXuxue Feng
Kanisha WaycottEcuadorSchroer, Gene E EsqXuxue Feng
Emerson BowleyFinlandKnights InnStephen Shaw
Blair MaletFinlandBollinger Mach Shp & ShipyardAsiya Javayant


<p-table [value]="customers" [scrollable]="true" scrollHeight="400px" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Name</th>
            <th>Country</th>
            <th>Company</th>
            <th>Representative</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>{{customer.name}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.representative.name}}</td>
        </tr>
    </ng-template>
</p-table>

Horizontal and vertical scroll can be used together to enable double axis scrolling.

IdNameCountryDateBalanceCompanyStatusActivityRepresentative
1000James ButtAlgeria2015-09-13$70,663.00Benton, John B Jrunqualified17Ioni Bowcher
1001Josephine DarakjyEgypt2019-02-09$82,429.00Chanay, Jeffrey A Esqproposal0Amy Elsner
1002Art VenerePanama2017-05-13$28,334.00Chemel, James L Cpaqualified63Asiya Javayant
1003Lenna PaprockiSlovenia2020-09-15$88,521.00Feltz Printing Servicenew37Xuxue Feng
1004Donette FollerSouth Africa2016-05-20$93,905.00Printing Dimensionsproposal33Asiya Javayant
1005Simona MorascaEgypt2018-02-16$50,041.00Chapman, Ross E Esqqualified68Ivan Magalhaes
1006Mitsue TollnerParaguay2018-02-19$58,706.00Morlong Associatesrenewal54Ivan Magalhaes
1007Leota DilliardSerbia2019-08-13$26,640.00Commercial Pressrenewal69Onyama Limba
1008Sage WieserEgypt2018-11-21$65,369.00Truhlar And Truhlar Attysunqualified76Ivan Magalhaes
1009Kris MarrierMexico2015-07-07$63,451.00King, Christopher A Esqproposal3Onyama Limba
1010Minna AmigonRomania2018-11-07$71,169.00Dorl, James J Esqqualified38Anna Fali
1011Abel MacleadSingapore2017-03-11$96,842.00Rangoni Of Florencequalified87Bernardo Dominic
1012Kiley CaldareraSerbia2015-10-20$92,734.00Feiner Brosunqualified80Onyama Limba
1013Graciela RutaChile2016-07-25$45,250.00Buckley Miller & Wrightnegotiation59Amy Elsner
1014Cammy AlbaresPhilippines2019-06-25$30,236.00Rousseaux, Michael Esqnew90Asiya Javayant
1015Mattie PoquetteVenezuela2017-12-12$64,533.00Century Communicationsnegotiation52Anna Fali
1016Meaghan GarufiMalaysia2018-07-04$37,279.00Bolton, Wilbur Esqunqualified31Ivan Magalhaes
1017Gladys RimNetherlands2020-02-27$27,381.00T M Byxbee Company Pcrenewal48Stephen Shaw
1018Yuki WhobreyIsrael2017-12-21$9,257.00Farmers Insurance Groupnegotiation16Bernardo Dominic
1019Fletcher FlosiArgentina2016-01-04$67,783.00Post Box Services Plusrenewal19Xuxue Feng
1020Bette NickaParaguay2016-10-21$4,609.00Sport En Artrenewal100Onyama Limba
1021Veronika InouyeEcuador2017-03-24$26,565.00C 4 Network Increnewal72Ioni Bowcher
1022Willard KolmetzTunisia2017-04-15$75,876.00Ingalls, Donald R Esqrenewal94Asiya Javayant
1023Maryann RoysterBelarus2017-03-11$41,121.00Franklin, Peter L Esqqualified56Elwin Sharvill
1024Alisha SlusarskiIceland2018-03-27$91,691.00Wtlz Power 107 Fmqualified7Stephen Shaw
1025Allene IturbideItaly2016-02-20$40,137.00Ledecky, David Esqqualified1Ivan Magalhaes
1026Chanel CaudyArgentina2018-06-24$21,304.00Professional Image Incnew26Ioni Bowcher
1027Ezekiel ChuiIreland2016-09-24$60,454.00Sider, Donald C Esqnew76Amy Elsner
1028Willow KuskoRomania2020-04-11$17,565.00U Pull Itqualified7Onyama Limba
1029Bernardo FigeroaIsrael2018-04-11$17,774.00Clark, Richard Cparenewal81Ioni Bowcher
1030Ammie CorrioHungary2016-06-11$49,201.00Moskowitz, Barry Snegotiation56Asiya Javayant
1031Francine VocelkaHonduras2017-08-02$67,126.00Cascade Realty Advisors Incqualified94Ioni Bowcher
1032Ernie StensethAustralia2018-06-06$76,017.00Knwz Newsradiorenewal68Xuxue Feng
1033Albina GlickUkraine2019-08-08$91,201.00Giampetro, Anthony Dproposal85Bernardo Dominic
1034Alishia SergiQatar2018-05-19$12,237.00Milford Enterprises Incnegotiation46Ivan Magalhaes
1035Solange ShinkoCameroon2015-02-12$34,072.00Mosocco, Ronald Aqualified32Onyama Limba
1036Jose StockhamItaly2018-04-25$94,909.00Tri State Refueler Coqualified77Amy Elsner
1037Rozella OstroskyVenezuela2016-02-27$57,245.00Parkway Companyunqualified66Amy Elsner
1038Valentine GillianParaguay2019-09-17$75,502.00Fbs Business Financequalified25Bernardo Dominic
1039Kati RulapaughPuerto Rico2016-12-03$82,075.00Eder Assocs Consltng Engrs Pcrenewal51Ioni Bowcher
1040Youlanda SchemmerBolivia2017-12-15$19,208.00Tri M Tool Incnegotiation49Xuxue Feng
1041Dyan OldroydArgentina2017-02-02$50,194.00International Eyelets Incqualified5Amy Elsner
1042Roxane CampainFrance2018-12-25$77,714.00Rapid Trading Intlunqualified100Anna Fali
1043Lavera PerinVietnam2018-04-10$35,740.00Abc Enterprises Incqualified71Stephen Shaw
1044Erick FerenczBelgium2018-05-06$30,790.00Cindy Turner Associatesunqualified54Amy Elsner
1045Fatima SaylorsCanada2019-07-10$52,343.00Stanton, James D Esqrenewal93Onyama Limba
1046Jina BriddickMexico2018-02-19$53,966.00Grace Pastries Incunqualified97Xuxue Feng
1047Kanisha WaycottEcuador2019-11-27$9,920.00Schroer, Gene E Esqnew80Xuxue Feng
1048Emerson BowleyFinland2018-11-24$78,069.00Knights Innnew63Stephen Shaw
1049Blair MaletFinland2018-04-19$65,005.00Bollinger Mach Shp & Shipyardnew92Asiya Javayant
IdNameCountryDateBalanceCompanyStatusActivityRepresentative


<p-table [value]="customers" [scrollable]="true" scrollHeight="400px">
    <ng-template pTemplate="header">
        <tr>
            <th style="min-width:100px">Id</th>
            <th style="min-width:200px">Name</th>
            <th style="min-width:200px">Country</th>
            <th style="min-width:200px">Date</th>
            <th style="min-width:200px">Balance</th>
            <th style="min-width:200px">Company</th>
            <th style="min-width:200px">Status</th>
            <th style="min-width:200px">Activity</th>
            <th style="min-width:200px">Representative</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>{{customer.id}}</td>
            <td>{{customer.name}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.date}}</td>
            <td>{{formatCurrency(customer.balance)}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.status}}</td>
            <td>{{customer.activity}}</td>
            <td>{{customer.representative.name}}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="footer">
        <tr>
            <td>Id</td>
            <td>Name</td>
            <td>Country</td>
            <td>Date</td>
            <td>Balance</td>
            <td>Company</td>
            <td>Status</td>
            <td>Activity</td>
            <td>Representative</td>
        </tr>
    </ng-template>
</p-table>

Flex scroll feature makes the scrollable viewport section dynamic instead of a fixed value so that it can grow or shrink relative to the parent size of the table. Click the button below to display a maximizable Dialog where data viewport adjusts itself according to the size changes.



<div class="flex justify-content-center">
    <button type="button" (click)="showDialog()" pButton icon="pi pi-external-link" label="View"></button>
</div>
<p-dialog header="Header" [resizable]="false" [modal]="true" [maximizable]="true" appendTo="body" [(visible)]="dialogVisible" [style]="{width: '75vw'}" [contentStyle]="{height: '300px'}">
    <p-table [value]="customers" [scrollable]="true" scrollHeight="flex" [tableStyle]="{'min-width': '50rem'}">
        <ng-template pTemplate="header">
            <tr>
                <th>Name</th>
                <th>Country</th>
                <th>Company</th>
                <th>Representative</th>
            </tr>
        </ng-template>
        <ng-template pTemplate="body" let-customer>
            <tr>
                <td>{{customer.name}}</td>
                <td>{{customer.country.name}}</td>
                <td>{{customer.company}}</td>
                <td>{{customer.representative.name}}</td>
            </tr>
        </ng-template>
    </p-table>
    <ng-template pTemplate="footer">
        <button type="button" pButton pRipple icon="pi pi-times" (click)="dialogVisible=false" label="Dismiss" class="p-button-text"></button>
    </ng-template>
</p-dialog>

Frozen rows are used to fix certain rows while scrolling, this data is defined with the frozenValue property.

NameCountryCompanyRepresentative
Geraldine BissetFranceBisset GroupAmy Elsner
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Simona MorascaEgyptChapman, Ross E EsqIvan Magalhaes
Mitsue TollnerParaguayMorlong AssociatesIvan Magalhaes
Leota DilliardSerbiaCommercial PressOnyama Limba
Sage WieserEgyptTruhlar And Truhlar AttysIvan Magalhaes
Kris MarrierMexicoKing, Christopher A EsqOnyama Limba
Minna AmigonRomaniaDorl, James J EsqAnna Fali
Abel MacleadSingaporeRangoni Of FlorenceBernardo Dominic
Kiley CaldareraSerbiaFeiner BrosOnyama Limba
Graciela RutaChileBuckley Miller & WrightAmy Elsner
Cammy AlbaresPhilippinesRousseaux, Michael EsqAsiya Javayant
Mattie PoquetteVenezuelaCentury CommunicationsAnna Fali
Meaghan GarufiMalaysiaBolton, Wilbur EsqIvan Magalhaes
Gladys RimNetherlandsT M Byxbee Company PcStephen Shaw
Yuki WhobreyIsraelFarmers Insurance GroupBernardo Dominic
Fletcher FlosiArgentinaPost Box Services PlusXuxue Feng
Bette NickaParaguaySport En ArtOnyama Limba
Veronika InouyeEcuadorC 4 Network IncIoni Bowcher
Willard KolmetzTunisiaIngalls, Donald R EsqAsiya Javayant
Maryann RoysterBelarusFranklin, Peter L EsqElwin Sharvill
Alisha SlusarskiIcelandWtlz Power 107 FmStephen Shaw
Allene IturbideItalyLedecky, David EsqIvan Magalhaes
Chanel CaudyArgentinaProfessional Image IncIoni Bowcher
Ezekiel ChuiIrelandSider, Donald C EsqAmy Elsner
Willow KuskoRomaniaU Pull ItOnyama Limba
Bernardo FigeroaIsraelClark, Richard CpaIoni Bowcher
Ammie CorrioHungaryMoskowitz, Barry SAsiya Javayant
Francine VocelkaHondurasCascade Realty Advisors IncIoni Bowcher
Ernie StensethAustraliaKnwz NewsradioXuxue Feng
Albina GlickUkraineGiampetro, Anthony DBernardo Dominic
Alishia SergiQatarMilford Enterprises IncIvan Magalhaes
Solange ShinkoCameroonMosocco, Ronald AOnyama Limba
Jose StockhamItalyTri State Refueler CoAmy Elsner
Rozella OstroskyVenezuelaParkway CompanyAmy Elsner
Valentine GillianParaguayFbs Business FinanceBernardo Dominic
Kati RulapaughPuerto RicoEder Assocs Consltng Engrs PcIoni Bowcher
Youlanda SchemmerBoliviaTri M Tool IncXuxue Feng
Dyan OldroydArgentinaInternational Eyelets IncAmy Elsner
Roxane CampainFranceRapid Trading IntlAnna Fali
Lavera PerinVietnamAbc Enterprises IncStephen Shaw
Erick FerenczBelgiumCindy Turner AssociatesAmy Elsner
Fatima SaylorsCanadaStanton, James D EsqOnyama Limba
Jina BriddickMexicoGrace Pastries IncXuxue Feng
Kanisha WaycottEcuadorSchroer, Gene E EsqXuxue Feng
Emerson BowleyFinlandKnights InnStephen Shaw
Blair MaletFinlandBollinger Mach Shp & ShipyardAsiya Javayant


<p-table [value]="unlockedCustomers" [frozenValue]="lockedCustomers" [scrollable]="true" scrollHeight="400px" [tableStyle]="{'min-width': '60rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Name</th>
            <th>Country</th>
            <th>Company</th>
            <th>Representative</th>
            <th style="width:5rem"></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="frozenbody" let-customer let-index="rowIndex">
        <tr>
            <td>{{customer.name}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.representative.name}}</td>
            <td>
                <button pButton pRipple type="button" [icon]="'pi pi-lock-open'" (click)="toggleLock(customer,true,index)" class="p-button-sm p-button-text"></button>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer let-index="rowIndex">
        <tr>
            <td>{{customer.name}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.representative.name}}</td>
            <td>
                <button pButton pRipple type="button" [icon]="'pi pi-lock'" [disabled]="lockedCustomers.length >= 2" (click)="toggleLock(customer,false,index)" class="p-button-sm p-button-text"></button>
            </td>
        </tr>
    </ng-template>
</p-table>

Certain columns can be frozen by using the pFrozenColumn directive of the table component. In addition, alignFrozen is available to define whether the column should be fixed on the left or right.

NameIdCountryDateCompanyStatusActivityRepresentativeBalance
James Butt1000Algeria2015-09-13Benton, John B Jrunqualified17Ioni Bowcher$70,663.00
Josephine Darakjy1001Egypt2019-02-09Chanay, Jeffrey A Esqproposal0Amy Elsner$82,429.00
Art Venere1002Panama2017-05-13Chemel, James L Cpaqualified63Asiya Javayant$28,334.00
Lenna Paprocki1003Slovenia2020-09-15Feltz Printing Servicenew37Xuxue Feng$88,521.00
Donette Foller1004South Africa2016-05-20Printing Dimensionsproposal33Asiya Javayant$93,905.00
Simona Morasca1005Egypt2018-02-16Chapman, Ross E Esqqualified68Ivan Magalhaes$50,041.00
Mitsue Tollner1006Paraguay2018-02-19Morlong Associatesrenewal54Ivan Magalhaes$58,706.00
Leota Dilliard1007Serbia2019-08-13Commercial Pressrenewal69Onyama Limba$26,640.00
Sage Wieser1008Egypt2018-11-21Truhlar And Truhlar Attysunqualified76Ivan Magalhaes$65,369.00
Kris Marrier1009Mexico2015-07-07King, Christopher A Esqproposal3Onyama Limba$63,451.00
Minna Amigon1010Romania2018-11-07Dorl, James J Esqqualified38Anna Fali$71,169.00
Abel Maclead1011Singapore2017-03-11Rangoni Of Florencequalified87Bernardo Dominic$96,842.00
Kiley Caldarera1012Serbia2015-10-20Feiner Brosunqualified80Onyama Limba$92,734.00
Graciela Ruta1013Chile2016-07-25Buckley Miller & Wrightnegotiation59Amy Elsner$45,250.00
Cammy Albares1014Philippines2019-06-25Rousseaux, Michael Esqnew90Asiya Javayant$30,236.00
Mattie Poquette1015Venezuela2017-12-12Century Communicationsnegotiation52Anna Fali$64,533.00
Meaghan Garufi1016Malaysia2018-07-04Bolton, Wilbur Esqunqualified31Ivan Magalhaes$37,279.00
Gladys Rim1017Netherlands2020-02-27T M Byxbee Company Pcrenewal48Stephen Shaw$27,381.00
Yuki Whobrey1018Israel2017-12-21Farmers Insurance Groupnegotiation16Bernardo Dominic$9,257.00
Fletcher Flosi1019Argentina2016-01-04Post Box Services Plusrenewal19Xuxue Feng$67,783.00
Bette Nicka1020Paraguay2016-10-21Sport En Artrenewal100Onyama Limba$4,609.00
Veronika Inouye1021Ecuador2017-03-24C 4 Network Increnewal72Ioni Bowcher$26,565.00
Willard Kolmetz1022Tunisia2017-04-15Ingalls, Donald R Esqrenewal94Asiya Javayant$75,876.00
Maryann Royster1023Belarus2017-03-11Franklin, Peter L Esqqualified56Elwin Sharvill$41,121.00
Alisha Slusarski1024Iceland2018-03-27Wtlz Power 107 Fmqualified7Stephen Shaw$91,691.00
Allene Iturbide1025Italy2016-02-20Ledecky, David Esqqualified1Ivan Magalhaes$40,137.00
Chanel Caudy1026Argentina2018-06-24Professional Image Incnew26Ioni Bowcher$21,304.00
Ezekiel Chui1027Ireland2016-09-24Sider, Donald C Esqnew76Amy Elsner$60,454.00
Willow Kusko1028Romania2020-04-11U Pull Itqualified7Onyama Limba$17,565.00
Bernardo Figeroa1029Israel2018-04-11Clark, Richard Cparenewal81Ioni Bowcher$17,774.00
Ammie Corrio1030Hungary2016-06-11Moskowitz, Barry Snegotiation56Asiya Javayant$49,201.00
Francine Vocelka1031Honduras2017-08-02Cascade Realty Advisors Incqualified94Ioni Bowcher$67,126.00
Ernie Stenseth1032Australia2018-06-06Knwz Newsradiorenewal68Xuxue Feng$76,017.00
Albina Glick1033Ukraine2019-08-08Giampetro, Anthony Dproposal85Bernardo Dominic$91,201.00
Alishia Sergi1034Qatar2018-05-19Milford Enterprises Incnegotiation46Ivan Magalhaes$12,237.00
Solange Shinko1035Cameroon2015-02-12Mosocco, Ronald Aqualified32Onyama Limba$34,072.00
Jose Stockham1036Italy2018-04-25Tri State Refueler Coqualified77Amy Elsner$94,909.00
Rozella Ostrosky1037Venezuela2016-02-27Parkway Companyunqualified66Amy Elsner$57,245.00
Valentine Gillian1038Paraguay2019-09-17Fbs Business Financequalified25Bernardo Dominic$75,502.00
Kati Rulapaugh1039Puerto Rico2016-12-03Eder Assocs Consltng Engrs Pcrenewal51Ioni Bowcher$82,075.00
Youlanda Schemmer1040Bolivia2017-12-15Tri M Tool Incnegotiation49Xuxue Feng$19,208.00
Dyan Oldroyd1041Argentina2017-02-02International Eyelets Incqualified5Amy Elsner$50,194.00
Roxane Campain1042France2018-12-25Rapid Trading Intlunqualified100Anna Fali$77,714.00
Lavera Perin1043Vietnam2018-04-10Abc Enterprises Incqualified71Stephen Shaw$35,740.00
Erick Ferencz1044Belgium2018-05-06Cindy Turner Associatesunqualified54Amy Elsner$30,790.00
Fatima Saylors1045Canada2019-07-10Stanton, James D Esqrenewal93Onyama Limba$52,343.00
Jina Briddick1046Mexico2018-02-19Grace Pastries Incunqualified97Xuxue Feng$53,966.00
Kanisha Waycott1047Ecuador2019-11-27Schroer, Gene E Esqnew80Xuxue Feng$9,920.00
Emerson Bowley1048Finland2018-11-24Knights Innnew63Stephen Shaw$78,069.00
Blair Malet1049Finland2018-04-19Bollinger Mach Shp & Shipyardnew92Asiya Javayant$65,005.00


<p-toggleButton [(ngModel)]="balanceFrozen" [onIcon]="'pi pi-lock'" offIcon="pi pi-lock-open" [onLabel]="'Balance'" offLabel="Balance"></p-toggleButton>

<p-table [value]="customers" [scrollable]="true" scrollHeight="400px" styleClass="mt-3">
    <ng-template pTemplate="header">
        <tr>
            <th style="min-width:200px" pFrozenColumn>Name</th>
            <th style="min-width:100px">Id</th>
            <th style="min-width:200px">Country</th>
            <th style="min-width:200px">Date</th>
            <th style="min-width:200px">Company</th>
            <th style="min-width:200px">Status</th>
            <th style="min-width:200px">Activity</th>
            <th style="min-width:200px">Representative</th>
            <th style="min-width:200px" alignFrozen="right" pFrozenColumn [frozen]="balanceFrozen">Balance</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td pFrozenColumn>{{customer.name}}</td>
            <td style="min-width:100px">{{customer.id}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.date}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.status}}</td>
            <td>{{customer.activity}}</td>
            <td >{{customer.representative.name}}</td>
            <td alignFrozen="right" pFrozenColumn [frozen]="balanceFrozen">{{formatCurrency(customer.balance)}}</td>
        </tr>
    </ng-template>
</p-table>

VirtualScroller is a performance-approach to handle huge data efficiently. Setting virtualScroll property as true and providing a virtualScrollItemSize in pixels would be enough to enable this functionality. It is also suggested to use the same virtualScrollItemSize value on the tr element inside the body template.

Id Vin Year Brand Color


<p-table [columns]="cols" [value]="cars" [scrollable]="true" scrollHeight="250px" [virtualScroll]="true" [virtualScrollItemSize]="46">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns" style="width: 20%;">
                {{ col.header }}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-rowIndex="rowIndex" let-columns="columns">
        <tr style="height:46px">
            <td *ngFor="let col of columns">
                {{ rowData[col.field] }}
            </td>
        </tr>
    </ng-template>
</p-table>

VirtualScroller is a performance-approach to handle huge data efficiently. Setting virtualScroll property as true and providing a virtualScrollItemSize in pixels would be enough to enable this functionality. It is also suggested to use the same virtualScrollItemSize value on the tr element inside the body template.

Id Vin Year Brand Color


<p-table [columns]="cols" [value]="virtualCars" [scrollable]="true" scrollHeight="250px" [rows]="100"
    [virtualScroll]="true" [virtualScrollItemSize]="46" [lazy]="true" (onLazyLoad)="loadCarsLazy($event)">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns" style="width: 20%;">
                {{col.header}}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns">
        <tr style="height:46px">
            <td *ngFor="let col of columns">
                {{rowData[col.field]}}
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="loadingbody" let-columns="columns">
        <tr style="height:46px">
            <td *ngFor="let col of columns; let even = even">
                <p-skeleton [ngStyle]="{'width': even ? (col.field === 'year' ? '30%' : '40%') : '60%'}"></p-skeleton>
            </td>
        </tr>
    </ng-template>
</p-table>

Columns can be grouped using rowspan and colspan properties.

ProductSale Rate
SalesProfits
Last YearThis YearLast YearThis Year
Bamboo Watch51%40%$54,406.00$43,342.00
Black Watch83%9%$423,132.00$312,122.00
Blue Band38%5%$12,321.00$8,500.00
Blue T-Shirt49%22%$745,232.00$65,323.00
Brown Purse17%79%$643,242.00$500,332.00
Chakra Bracelet52%65%$421,132.00$150,005.00
Galaxy Earrings82%12%$131,211.00$100,214.00
Game Controller44%45%$66,442.00$53,322.00
Gaming Set90%56%$765,442.00$296,232.00
Gold Phone Case75%54%$21,212.00$12,533.00
Totals$3,283,772.00$1,541,925.00


<p-table [value]="sales" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th rowspan="3">Product</th>
            <th colspan="4">Sale Rate</th>
        </tr>
        <tr>
            <th colspan="2">Sales</th>
            <th colspan="2">Profits</th>
        </tr>
        <tr>
            <th>Last Year</th>
            <th>This Year</th>
            <th>Last Year</th>
            <th>This Year</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-sale>
        <tr>
            <td>{{sale.product}}</td>
            <td>{{sale.lastYearSale}}%</td>
            <td>{{sale.thisYearSale}}%</td>
            <td>{{sale.lastYearProfit | currency: 'USD'}}</td>
            <td>{{sale.thisYearProfit | currency: 'USD'}}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="footer">
        <tr>
            <td colspan="3" class="text-right">Totals</td>
            <td>{{lastYearTotal | currency: 'USD'}}</td>
            <td>{{thisYearTotal | currency: 'USD'}}</td>
        </tr>
    </ng-template>
</p-table>

Rows are grouped with the groupRowsBy property. When rowGroupMode is set as subheader, a header and footer can be displayed for each group. The content of a group header is provided with groupheader and footer with groupfooter templates.

NameCountryCompanyStatusDate
Amy ElsnerAmy Elsner
Josephine Darakjy Egypt Chanay, Jeffrey A Esq proposal 2019-02-09
Graciela Ruta Chile Buckley Miller & Wright negotiation 2016-07-25
Ezekiel Chui Ireland Sider, Donald C Esq new 2016-09-24
Jose Stockham Italy Tri State Refueler Co qualified 2018-04-25
Rozella Ostrosky Venezuela Parkway Company unqualified 2016-02-27
Dyan Oldroyd Argentina International Eyelets Inc qualified 2017-02-02
Erick Ferencz Belgium Cindy Turner Associates unqualified 2018-05-06
Total Customers: 7
Anna FaliAnna Fali
Minna Amigon Romania Dorl, James J Esq qualified 2018-11-07
Mattie Poquette Venezuela Century Communications negotiation 2017-12-12
Roxane Campain France Rapid Trading Intl unqualified 2018-12-25
Total Customers: 3
Asiya JavayantAsiya Javayant
Art Venere Panama Chemel, James L Cpa qualified 2017-05-13
Donette Foller South Africa Printing Dimensions proposal 2016-05-20
Cammy Albares Philippines Rousseaux, Michael Esq new 2019-06-25
Willard Kolmetz Tunisia Ingalls, Donald R Esq renewal 2017-04-15
Ammie Corrio Hungary Moskowitz, Barry S negotiation 2016-06-11
Blair Malet Finland Bollinger Mach Shp & Shipyard new 2018-04-19
Total Customers: 6
Bernardo DominicBernardo Dominic
Abel Maclead Singapore Rangoni Of Florence qualified 2017-03-11
Yuki Whobrey Israel Farmers Insurance Group negotiation 2017-12-21
Albina Glick Ukraine Giampetro, Anthony D proposal 2019-08-08
Valentine Gillian Paraguay Fbs Business Finance qualified 2019-09-17
Total Customers: 4
Elwin SharvillElwin Sharvill
Maryann Royster Belarus Franklin, Peter L Esq qualified 2017-03-11
Total Customers: 1
Ioni BowcherIoni Bowcher
James Butt Algeria Benton, John B Jr unqualified 2015-09-13
Veronika Inouye Ecuador C 4 Network Inc renewal 2017-03-24
Chanel Caudy Argentina Professional Image Inc new 2018-06-24
Bernardo Figeroa Israel Clark, Richard Cpa renewal 2018-04-11
Francine Vocelka Honduras Cascade Realty Advisors Inc qualified 2017-08-02
Kati Rulapaugh Puerto Rico Eder Assocs Consltng Engrs Pc renewal 2016-12-03
Total Customers: 6
Ivan MagalhaesIvan Magalhaes
Simona Morasca Egypt Chapman, Ross E Esq qualified 2018-02-16
Mitsue Tollner Paraguay Morlong Associates renewal 2018-02-19
Sage Wieser Egypt Truhlar And Truhlar Attys unqualified 2018-11-21
Meaghan Garufi Malaysia Bolton, Wilbur Esq unqualified 2018-07-04
Allene Iturbide Italy Ledecky, David Esq qualified 2016-02-20
Alishia Sergi Qatar Milford Enterprises Inc negotiation 2018-05-19
Total Customers: 6
Onyama LimbaOnyama Limba
Leota Dilliard Serbia Commercial Press renewal 2019-08-13
Kris Marrier Mexico King, Christopher A Esq proposal 2015-07-07
Kiley Caldarera Serbia Feiner Bros unqualified 2015-10-20
Bette Nicka Paraguay Sport En Art renewal 2016-10-21
Willow Kusko Romania U Pull It qualified 2020-04-11
Solange Shinko Cameroon Mosocco, Ronald A qualified 2015-02-12
Fatima Saylors Canada Stanton, James D Esq renewal 2019-07-10
Total Customers: 7
Stephen ShawStephen Shaw
Gladys Rim Netherlands T M Byxbee Company Pc renewal 2020-02-27
Alisha Slusarski Iceland Wtlz Power 107 Fm qualified 2018-03-27
Lavera Perin Vietnam Abc Enterprises Inc qualified 2018-04-10
Emerson Bowley Finland Knights Inn new 2018-11-24
Total Customers: 4
Xuxue FengXuxue Feng
Lenna Paprocki Slovenia Feltz Printing Service new 2020-09-15
Fletcher Flosi Argentina Post Box Services Plus renewal 2016-01-04
Ernie Stenseth Australia Knwz Newsradio renewal 2018-06-06
Youlanda Schemmer Bolivia Tri M Tool Inc negotiation 2017-12-15
Jina Briddick Mexico Grace Pastries Inc unqualified 2018-02-19
Kanisha Waycott Ecuador Schroer, Gene E Esq new 2019-11-27
Total Customers: 6


<p-table [value]="customers" sortField="representative.name" sortMode="single" [scrollable]="true" scrollHeight="400px" rowGroupMode="subheader" groupRowsBy="representative.name" [tableStyle]="{'min-width': '60rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Name</th>
            <th>Country</th>
            <th>Company</th>
            <th>Status</th>
            <th>Date</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="groupheader" let-customer>
        <tr pRowGroupHeader>
            <td colspan="5">
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{customer.representative.image}}" width="32" style="vertical-align: middle" />
                <span class="font-bold ml-2">{{customer.representative.name}}</span>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="groupfooter" let-customer>
        <tr>
            <td colspan="5" class="text-right font-bold pr-6">Total Customers: {{calculateCustomerTotal(customer.representative.name)}}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer let-rowIndex="rowIndex">
        <tr>
            <td>
                {{customer.name}}
            </td>
            <td>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px">
                <span class="ml-1 vertical-align-middle">{{customer.country.name}}</span>
            </td>
            <td>
                {{customer.company}}
            </td>
            <td>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                {{customer.date}}
            </td>
        </tr>
    </ng-template>
</p-table>

When expandableRowGroups is present in subheader based row grouping, groups can be expanded and collapsed. State of the expansions are controlled using the expandedRows and onRowToggle properties.

NameCountryCompanyStatusDate
Amy ElsnerAmy Elsner
Anna FaliAnna Fali
Asiya JavayantAsiya Javayant
Bernardo DominicBernardo Dominic
Elwin SharvillElwin Sharvill
Ioni BowcherIoni Bowcher
Ivan MagalhaesIvan Magalhaes
Onyama LimbaOnyama Limba
Stephen ShawStephen Shaw
Xuxue FengXuxue Feng


<p-table [value]="customers" sortField="representative.name" sortMode="single" dataKey="representative.name" rowGroupMode="subheader" groupRowsBy="representative.name" [tableStyle]="{'min-width': '70rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width:20%">Name</th>
            <th style="width:20%">Country</th>
            <th style="width:20%">Company</th>
            <th style="width:20%">Status</th>
            <th style="width:20%">Date</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="groupheader" let-customer let-rowIndex="rowIndex" let-expanded="expanded">
        <tr>
            <td colspan="5">
                <button type="button" pButton pRipple [pRowToggler]="customer" class="p-button-text p-button-rounded p-button-plain mr-2" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{customer.representative.image}}" width="32" style="vertical-align: middle" />
                <span class="font-bold ml-2">{{customer.representative.name}}</span>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="groupfooter" let-customer>
        <tr class="p-rowgroup-footer">
            <td colspan="4" style="text-align: right">Total Customers</td>
            <td>{{calculateCustomerTotal(customer.representative.name)}}</td>
        </tr>
    </ng-template>
    <ng-template pTemplate="rowexpansion" let-customer>
        <tr>
            <td>
                {{customer.name}}
            </td>
            <td>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px">
                <span class="ml-1 vertical-align-middle">{{customer.country.name}}</span>
            </td>
            <td>
                {{customer.company}}
            </td>
            <td>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                {{customer.date}}
            </td>
        </tr>
    </ng-template>
</p-table>

When rowGroupMode is configured to be rowspan, the grouping column spans multiple rows.

#RepresentativeNameCountryCompanyStatusDate
0Amy ElsnerAmy Elsner Josephine Darakjy Egypt Chanay, Jeffrey A Esq proposal 2019-02-09
1 Graciela Ruta Chile Buckley Miller & Wright negotiation 2016-07-25
2 Ezekiel Chui Ireland Sider, Donald C Esq new 2016-09-24
3 Jose Stockham Italy Tri State Refueler Co qualified 2018-04-25
4 Rozella Ostrosky Venezuela Parkway Company unqualified 2016-02-27
5 Dyan Oldroyd Argentina International Eyelets Inc qualified 2017-02-02
6 Erick Ferencz Belgium Cindy Turner Associates unqualified 2018-05-06
7Anna FaliAnna Fali Minna Amigon Romania Dorl, James J Esq qualified 2018-11-07
8 Mattie Poquette Venezuela Century Communications negotiation 2017-12-12
9 Roxane Campain France Rapid Trading Intl unqualified 2018-12-25
10Asiya JavayantAsiya Javayant Art Venere Panama Chemel, James L Cpa qualified 2017-05-13
11 Donette Foller South Africa Printing Dimensions proposal 2016-05-20
12 Cammy Albares Philippines Rousseaux, Michael Esq new 2019-06-25
13 Willard Kolmetz Tunisia Ingalls, Donald R Esq renewal 2017-04-15
14 Ammie Corrio Hungary Moskowitz, Barry S negotiation 2016-06-11
15 Blair Malet Finland Bollinger Mach Shp & Shipyard new 2018-04-19
16Bernardo DominicBernardo Dominic Abel Maclead Singapore Rangoni Of Florence qualified 2017-03-11
17 Yuki Whobrey Israel Farmers Insurance Group negotiation 2017-12-21
18 Albina Glick Ukraine Giampetro, Anthony D proposal 2019-08-08
19 Valentine Gillian Paraguay Fbs Business Finance qualified 2019-09-17
20Elwin SharvillElwin Sharvill Maryann Royster Belarus Franklin, Peter L Esq qualified 2017-03-11
21Ioni BowcherIoni Bowcher James Butt Algeria Benton, John B Jr unqualified 2015-09-13
22 Veronika Inouye Ecuador C 4 Network Inc renewal 2017-03-24
23 Chanel Caudy Argentina Professional Image Inc new 2018-06-24
24 Bernardo Figeroa Israel Clark, Richard Cpa renewal 2018-04-11
25 Francine Vocelka Honduras Cascade Realty Advisors Inc qualified 2017-08-02
26 Kati Rulapaugh Puerto Rico Eder Assocs Consltng Engrs Pc renewal 2016-12-03
27Ivan MagalhaesIvan Magalhaes Simona Morasca Egypt Chapman, Ross E Esq qualified 2018-02-16
28 Mitsue Tollner Paraguay Morlong Associates renewal 2018-02-19
29 Sage Wieser Egypt Truhlar And Truhlar Attys unqualified 2018-11-21
30 Meaghan Garufi Malaysia Bolton, Wilbur Esq unqualified 2018-07-04
31 Allene Iturbide Italy Ledecky, David Esq qualified 2016-02-20
32 Alishia Sergi Qatar Milford Enterprises Inc negotiation 2018-05-19
33Onyama LimbaOnyama Limba Leota Dilliard Serbia Commercial Press renewal 2019-08-13
34 Kris Marrier Mexico King, Christopher A Esq proposal 2015-07-07
35 Kiley Caldarera Serbia Feiner Bros unqualified 2015-10-20
36 Bette Nicka Paraguay Sport En Art renewal 2016-10-21
37 Willow Kusko Romania U Pull It qualified 2020-04-11
38 Solange Shinko Cameroon Mosocco, Ronald A qualified 2015-02-12
39 Fatima Saylors Canada Stanton, James D Esq renewal 2019-07-10
40Stephen ShawStephen Shaw Gladys Rim Netherlands T M Byxbee Company Pc renewal 2020-02-27
41 Alisha Slusarski Iceland Wtlz Power 107 Fm qualified 2018-03-27
42 Lavera Perin Vietnam Abc Enterprises Inc qualified 2018-04-10
43 Emerson Bowley Finland Knights Inn new 2018-11-24
44Xuxue FengXuxue Feng Lenna Paprocki Slovenia Feltz Printing Service new 2020-09-15
45 Fletcher Flosi Argentina Post Box Services Plus renewal 2016-01-04
46 Ernie Stenseth Australia Knwz Newsradio renewal 2018-06-06
47 Youlanda Schemmer Bolivia Tri M Tool Inc negotiation 2017-12-15
48 Jina Briddick Mexico Grace Pastries Inc unqualified 2018-02-19
49 Kanisha Waycott Ecuador Schroer, Gene E Esq new 2019-11-27


<p-table [value]="customers" rowGroupMode="rowspan" groupRowsBy="representative.name" sortField="representative.name" sortMode="single"  [tableStyle]="{'min-width': '75rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th style="width:3rem">#</th>
            <th>Representative</th>
            <th>Name</th>
            <th>Country</th>
            <th>Company</th>
            <th>Status</th>
            <th>Date</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer let-rowIndex="rowIndex" let-rowgroup="rowgroup" let-rowspan="rowspan">
        <tr>
            <td>{{rowIndex}}</td>
            <td *ngIf="rowgroup" [attr.rowspan]="rowspan">
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{customer.representative.image}}" width="32" style="vertical-align: middle" />
                <span class="font-bold ml-2">{{customer.representative.name}}</span>
            </td>
            <td>
                {{customer.name}}
            </td>
            <td>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px">
                <span class="ml-1 vertical-align-middle">{{customer.country.name}}</span>
            </td>
            <td>
                {{customer.company}}
            </td>
            <td>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                {{customer.date}}
            </td>
        </tr>
    </ng-template>
</p-table>

Columns can be resized using drag drop by setting the resizableColumns to true. Fit mode is the default one and the overall table width does not change when a column is resized.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [resizableColumns]="true" styleClass="p-datatable-gridlines" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th pResizableColumn>Code</th>
            <th pResizableColumn>Name</th>
            <th pResizableColumn>Category</th>
            <th pResizableColumn>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

Setting columnResizeMode as expand changes the table width as well.

CodeNameCategoryQuantity
f230fh0g3Bamboo WatchAccessories24
nvklal433Black WatchAccessories61
zz21cz3c1Blue BandFitness2
244wgerg2Blue T-ShirtClothing25
h456wer53BraceletAccessories73


<p-table [value]="products" [resizableColumns]="true" columnResizeMode="expand" styleClass="p-datatable-gridlines" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th pResizableColumn>Code</th>
            <th pResizableColumn>Name</th>
            <th pResizableColumn>Category</th>
            <th pResizableColumn>Quantity</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr>
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.quantity}}</td>
        </tr>
    </ng-template>
</p-table>

NameCountryCompanyRepresentative
James ButtAlgeriaBenton, John B JrIoni Bowcher
Josephine DarakjyEgyptChanay, Jeffrey A EsqAmy Elsner
Art VenerePanamaChemel, James L CpaAsiya Javayant
Lenna PaprockiSloveniaFeltz Printing ServiceXuxue Feng
Donette FollerSouth AfricaPrinting DimensionsAsiya Javayant
Simona MorascaEgyptChapman, Ross E EsqIvan Magalhaes
Mitsue TollnerParaguayMorlong AssociatesIvan Magalhaes
Leota DilliardSerbiaCommercial PressOnyama Limba
Sage WieserEgyptTruhlar And Truhlar AttysIvan Magalhaes
Kris MarrierMexicoKing, Christopher A EsqOnyama Limba
Minna AmigonRomaniaDorl, James J EsqAnna Fali
Abel MacleadSingaporeRangoni Of FlorenceBernardo Dominic
Kiley CaldareraSerbiaFeiner BrosOnyama Limba
Graciela RutaChileBuckley Miller & WrightAmy Elsner
Cammy AlbaresPhilippinesRousseaux, Michael EsqAsiya Javayant
Mattie PoquetteVenezuelaCentury CommunicationsAnna Fali
Meaghan GarufiMalaysiaBolton, Wilbur EsqIvan Magalhaes
Gladys RimNetherlandsT M Byxbee Company PcStephen Shaw
Yuki WhobreyIsraelFarmers Insurance GroupBernardo Dominic
Fletcher FlosiArgentinaPost Box Services PlusXuxue Feng
Bette NickaParaguaySport En ArtOnyama Limba
Veronika InouyeEcuadorC 4 Network IncIoni Bowcher
Willard KolmetzTunisiaIngalls, Donald R EsqAsiya Javayant
Maryann RoysterBelarusFranklin, Peter L EsqElwin Sharvill
Alisha SlusarskiIcelandWtlz Power 107 FmStephen Shaw
Allene IturbideItalyLedecky, David EsqIvan Magalhaes
Chanel CaudyArgentinaProfessional Image IncIoni Bowcher
Ezekiel ChuiIrelandSider, Donald C EsqAmy Elsner
Willow KuskoRomaniaU Pull ItOnyama Limba
Bernardo FigeroaIsraelClark, Richard CpaIoni Bowcher
Ammie CorrioHungaryMoskowitz, Barry SAsiya Javayant
Francine VocelkaHondurasCascade Realty Advisors IncIoni Bowcher
Ernie StensethAustraliaKnwz NewsradioXuxue Feng
Albina GlickUkraineGiampetro, Anthony DBernardo Dominic
Alishia SergiQatarMilford Enterprises IncIvan Magalhaes
Solange ShinkoCameroonMosocco, Ronald AOnyama Limba
Jose StockhamItalyTri State Refueler CoAmy Elsner
Rozella OstroskyVenezuelaParkway CompanyAmy Elsner
Valentine GillianParaguayFbs Business FinanceBernardo Dominic
Kati RulapaughPuerto RicoEder Assocs Consltng Engrs PcIoni Bowcher
Youlanda SchemmerBoliviaTri M Tool IncXuxue Feng
Dyan OldroydArgentinaInternational Eyelets IncAmy Elsner
Roxane CampainFranceRapid Trading IntlAnna Fali
Lavera PerinVietnamAbc Enterprises IncStephen Shaw
Erick FerenczBelgiumCindy Turner AssociatesAmy Elsner
Fatima SaylorsCanadaStanton, James D EsqOnyama Limba
Jina BriddickMexicoGrace Pastries IncXuxue Feng
Kanisha WaycottEcuadorSchroer, Gene E EsqXuxue Feng
Emerson BowleyFinlandKnights InnStephen Shaw
Blair MaletFinlandBollinger Mach Shp & ShipyardAsiya Javayant
Brock BologniaBoliviaOrinda NewsOnyama Limba
Lorrie NestleGermanyBallard Spahr AndrewsAnna Fali
Sabra UyetakePeruLowy Limousine ServiceAmy Elsner
Marjory MastellaNetherlandsVicon CorporationAnna Fali
Karl KlonowskiSaudi ArabiaRossi, Michael MOnyama Limba
Tonette WennerAustraliaNorthwest PublishingElwin Sharvill
Amber MonarrezSwedenBranford Wire & Mfg CoBernardo Dominic
Shenika SeewaldAustraliaEast Coast MarketingXuxue Feng
Delmy AhleBelgiumWye Technologies IncAnna Fali
Deeanna JuhasSwedenHealy, George W IvAsiya Javayant
Blondell PughIrelandAlpenlite IncBernardo Dominic
Jamal VanausdalMoroccoHubbard, Bruce EsqIoni Bowcher
Cecily HollackBoliviaArthur A Oliver & Son IncAmy Elsner
Carmelina LindallPuerto RicoGeorge Jessop Carter JewelersAsiya Javayant
Maurine YglesiasTaiwanSchultz, Thomas C MdIoni Bowcher
Tawna BuvensIndonesiaH H H Enterprises IncAmy Elsner
Penney WeightSouth AfricaHawaiian King HotelAmy Elsner
Elly MoroccoThailandKillion IndustriesXuxue Feng
Ilene EromanNetherlandsRobinson, William J EsqAnna Fali
Vallie MondellaLatviaPrivate PropertiesIvan Magalhaes
Kallie BlackwoodIcelandRowley Schlimgen IncAmy Elsner
Johnetta AbdallahNetherlandsForging SpecialtiesElwin Sharvill
Bobbye RhymUkraineSmits, Patricia GarityXuxue Feng
Micaela RhymesFranceH Lee Leonard Attorney At LawAsiya Javayant
Tamar HooglandGuatemalaA K Construction CoAsiya Javayant
Moon ParlatoCzech RepublicAmbelang, Jessica M MdOnyama Limba
Laurel ReitlerUnited KingdomQ A ServiceAmy Elsner
Delisa CrupiTaiwanWood & Whitacre ContractorsXuxue Feng
Viva ToelkesUnited StatesMark Iv Press LtdStephen Shaw
Elza LipkeIrelandMuseum Of Science & IndustryElwin Sharvill
Devorah ChickeringSpainGarrison IndAsiya Javayant
Timothy MulqueenNetherlandsSaronix Nymph ProductsAsiya Javayant
Arlette HoneywellPanamaSmc IncAmy Elsner
Dominque DickersonArgentinaE A I Electronic Assocs IncBernardo Dominic
Lettie IsenhowerCanadaConte, Christopher A EsqBernardo Dominic
Myra MunnsLithuaniaAnker Law OfficeElwin Sharvill
Stephaine BarfieldBelgiumBeutelschies & CompanyAnna Fali
Lai GatoNigeriaFligg, Kenneth I JrOnyama Limba
Stephen EmighCubaSharp, J Daniel EsqElwin Sharvill
Tyra ShieldsHondurasAssink, Anne H EsqAnna Fali
Tammara WardripSaudi ArabiaJewel My Shop IncXuxue Feng
Cory GibesMalaysiaChinese Translation ResourcesAnna Fali
Danica BruschkeTaiwanStevens, Charles TStephen Shaw
Wilda GiguereIcelandMclaughlin, Luther W CpaAsiya Javayant
Elvera BenimadhoMalaysiaTree MusketeersOnyama Limba
Carma VanheusenTurkeySpringfield Div Oh Edison CoStephen Shaw
Malinda HochardSerbiaLogan Memorial HospitalAsiya Javayant
Natalie FernCanadaKelly, Charles G EsqAmy Elsner
Lisha CentiniNetherlandsIndustrial Paper Shredders IncIoni Bowcher
Arlene KlusmanJamaicaBeck Horizon BuildersElwin Sharvill
Alease BuemiCosta RicaPorto Cayo At Hawks CayOnyama Limba
Louisa CronauerCosta RicaPacific Grove Museum Ntrl HistAnna Fali
Angella CettaVietnamBender & Hatley PcIvan Magalhaes
Cyndy GoldammerBurkina FasoDi Cristina J & SonStephen Shaw
Rosio CorkSingaporeGreen GoddessAsiya Javayant
Celeste KorandoCosta RicaAmerican Arts & GraphicsAmy Elsner
Twana FelgerCroatiaOpryland HotelIoni Bowcher
Estrella SamuVietnamMarking Devices Pubg CoBernardo Dominic
Donte KinesSlovakiaW Tc Industries IncOnyama Limba
Tiffiny SteffensmeierPakistanWhitehall Robbins Labs DivsnIvan Magalhaes
Edna MiceliFranceSamplerAsiya Javayant
Sue KownackiJamaicaJuno Chefs IncorporatedOnyama Limba
Jesusa ShinUkraineCarroccio, A Thomas EsqBernardo Dominic
Rolland FrancesconUnited KingdomStanley, Richard L EsqOnyama Limba
Pamella SchmiererBelgiumK Cs Cstm Mouldings WindowsIoni Bowcher
Glory KulzerCroatiaComfort InnOnyama Limba
Shawna PalaspasEstoniaWindsor, James L EsqBernardo Dominic
Brandon CallaroRomaniaJackson Shields YeiserAnna Fali
Scarlet CartanPanamaBox, J Calvin EsqXuxue Feng
Oretha MenterPanamaCustom Engineering IncElwin Sharvill
Ty SmithUnited StatesBresler Eitel Framg Gllry LtdAnna Fali
Xuan RochinColombiaCarol, Drake Sparks EsqAmy Elsner
Lindsey DilelloAustriaBiltmore Investors BankAmy Elsner
Devora PerezUruguayDesco Equipment CorpOnyama Limba
Herman DemesaParaguayMerlin Electric CoAsiya Javayant
Rory PapasergiEgyptBailey Cntl Co Div BabcockAnna Fali
Talia RiopelleGuatemalaFord Brothers Wholesale IncElwin Sharvill
Van ShireNetherlandsCambridge InnIoni Bowcher
Lucina LarySwitzerlandMatricciani, Albert J JrXuxue Feng
Bok IsaacsChileNelson Hawaiian LtdAsiya Javayant
Rolande SpickermanPanamaNeland Travel AgencyBernardo Dominic
Howard PaulasIndonesiaAsendorf, J Alan EsqIoni Bowcher
Kimbery MadarangSenegalSilberman, Arthur L EsqOnyama Limba
Thurman MannoColombiaHoney Bee Breeding Genetics &Ivan Magalhaes
Becky MirafuentesSerbiaWells Kravitz SchnitzerElwin Sharvill
Beatriz CorringtonSouth AfricaProhab Rehabilitation ServsStephen Shaw
Marti MayburyThailandEldridge, Kristin K EsqBernardo Dominic
Nieves GotterLatviaVlahos, John J EsqElwin Sharvill
Leatha HageleUkraineNinas Indian Grs & VideosStephen Shaw
Valentin KlimekIvory CoastSchmid, Gayanne K EsqIvan Magalhaes
Melissa WiklundJapanMoapa Valley Federal Credit UnOnyama Limba
Sheridan ZaneCroatiaKentucky Tennessee Clay CoBernardo Dominic
Bulah PadillaPhilippinesAdmiral Party Rentals & SalesIoni Bowcher
Audra KohnertNetherlandsNelson, Karolyn King EsqBernardo Dominic
Daren WeiratherIsraelPanasystemsOnyama Limba
Fernanda JillsonMexicoShank, Edward L EsqXuxue Feng
Gearldine GellingerEgyptMegibow & EdwardsAnna Fali
Chau KitzmanParaguayBenoff, Edward EsqOnyama Limba
Theola FreyVietnamWoodbridge Free Public LibraryIoni Bowcher
Cheryl HaroldsonFranceNew York Life John ThuneElwin Sharvill
Laticia MercedBurkina FasoAlinabal IncIvan Magalhaes
Carissa BatmanGreecePoletto, Kim David EsqIvan Magalhaes
Lezlie CragheadPanamaChang, Carolyn EsqXuxue Feng
Ozell ShealyPakistanSilver Bros IncBernardo Dominic
Arminda ParvisIndonesiaNewtec IncElwin Sharvill
Reita LetoBelgiumCreative Business SystemsIoni Bowcher
Yolando LuczkiFranceDal Tile CorporationIoni Bowcher
Lizette StemSlovakiaEdward S KatzStephen Shaw
Gregoria PawlowiczEgyptOh My Goodknits IncStephen Shaw
Carin DeleoChinaRedeker, DebbieAsiya Javayant
Chantell MaynerichEstoniaDesert Sands MotelIvan Magalhaes
Dierdre YumCzech RepublicCummins Southern Plains IncOnyama Limba
Larae GudroeSloveniaLehigh Furn Divsn LehighIoni Bowcher
Latrice TolfreeJamaicaUnited Van Lines AgentIoni Bowcher
Kerry TheodorovRomaniaCapitol ReportersAmy Elsner
Dorthy HidvegiPolandKwik Kopy PrintingIvan Magalhaes
Fannie LungrenBelarusCentro IncStephen Shaw
Evangelina RaddeIvory CoastCampbell, Jan EsqAnna Fali
Novella DegrootSloveniaEvans, C Kelly EsqAmy Elsner
Clay HoaParaguayScat EnterprisesAmy Elsner
Jennifer FallickAustraliaNagle, Daniel J EsqBernardo Dominic
Irma WolfgrammBelgiumSerendiquity Bed & BreakfastStephen Shaw
Eun CoodyTaiwanRay Carolyne RealtyIoni Bowcher
Sylvia CouseyIrelandBerg, Charles EIoni Bowcher
Nana WrinklesAustriaRay, Milbern DAsiya Javayant
Layla SpringeSouth AfricaChadds Ford WineryIoni Bowcher
Joesph DegoniaSerbiaA R PackagingElwin Sharvill
Annabelle BoordGuatemalaCorn PopperAnna Fali
Stephaine VinningAustraliaBirite Foodservice DistrXuxue Feng
Nelida SawchukSouth AfricaAnchorage Museum Of Hist & ArtOnyama Limba
Marguerita HiattUnited KingdomHaber, George D MdAnna Fali
Carmela CookeyFranceRoyal Pontiac Olds IncXuxue Feng
Junita BrideauIndonesiaLeonards Antiques IncAnna Fali
Claribel VarrianoEcuadorMecaOnyama Limba
Benton SkurskyIcelandNercon Engineering & Mfg IncAsiya Javayant
Hillary SkulskiFranceReplica IBernardo Dominic
Merilyn BaylessJamaica20 20 Printing IncIvan Magalhaes
Teri EnnacoPakistanPublishers Group WestBernardo Dominic
Merlyn LawlerGermanyNischwitz, Jeffrey L EsqIvan Magalhaes
Georgene MontezumaSenegalPayne Blades & Wellborn PaElwin Sharvill
Jettie MconnellDenmarkColdwell Bnkr Wright Real EstIvan Magalhaes
Lemuel LatzkeColombiaComputer Repair ServiceStephen Shaw
Melodie KnippFinlandFleetwood Building Block IncAsiya Javayant
Candida CorbleyPolandColts Neck Medical Assocs IncOnyama Limba
Karan KarpinEstoniaNew England TaxidermyStephen Shaw
Andra ScheyerRomaniaLudcke, George O EsqElwin Sharvill
Felicidad PoullionGreeceMccorkle, Tom S EsqElwin Sharvill
Belen StrassnerIvory CoastEagle Software IncXuxue Feng
Gracia MelnykCosta RicaJuvenile & Adult SuperAsiya Javayant
Jolanda HanafanCameroonPerez, Joseph J EsqIvan Magalhaes


<p-table [value]="customers" [scrollable]="true" scrollHeight="400px" [resizableColumns]="true" styleClass="p-datatable-gridlines" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th pResizableColumn>Name</th>
            <th pResizableColumn>Country</th>
            <th pResizableColumn>Company</th>
            <th pResizableColumn>Representative</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr>
            <td>{{customer.name}}</td>
            <td>{{customer.country.name}}</td>
            <td>{{customer.company}}</td>
            <td>{{customer.representative.name}}</td>
        </tr>
    </ng-template>
</p-table>

Order of the columns and rows can be changed using drag and drop. Column reordering is configured by adding reorderableColumns property.

Similarly, adding reorderableRows property enables draggable rows. For the drag handle a column needs to have rowReorder property and onRowReorder callback is required to control the state of the rows after reorder completes.

Code Name Category Quantity
f230fh0g3 Bamboo Watch Accessories 24
nvklal433 Black Watch Accessories 61
zz21cz3c1 Blue Band Fitness 2
244wgerg2 Blue T-Shirt Clothing 25
h456wer53 Bracelet Accessories 73
av2231fwg Brown Purse Accessories 0
bib36pfvm Chakra Bracelet Accessories 5
mbvjkgip5 Galaxy Earrings Accessories 23
vbb124btr Game Controller Electronics 2
cm230f032 Gaming Set Electronics 63


<p-table [value]="products" [columns]="cols" [reorderableColumns]="true" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th style="width:3rem"></th>
            <th *ngFor="let col of columns" pReorderableColumn>
                {{col.header}}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns" let-index="rowIndex">
        <tr [pReorderableRow]="index">
            <td>
                <span class="pi pi-bars" pReorderableRowHandle></span>
            </td>
            <td *ngFor="let col of columns">
                {{rowData[col.field]}}
            </td>
        </tr>
    </ng-template>
</p-table>

This demo uses a multiselect component to implement toggleable columns.

Name, Category, Quantity
Code Name Category Quantity
f230fh0g3 Bamboo Watch Accessories 24
nvklal433 Black Watch Accessories 61
zz21cz3c1 Blue Band Fitness 2
244wgerg2 Blue T-Shirt Clothing 25
h456wer53 Bracelet Accessories 73


<p-table [columns]="selectedColumns" [value]="products" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="caption">
        <p-multiSelect [options]="cols" [(ngModel)]="selectedColumns" optionLabel="header"
            selectedItemsLabel="{0} columns selected" [style]="{'min-width': '200px'}" placeholder="Choose Columns"></p-multiSelect>
    </ng-template>
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th>Code</th>
            <th *ngFor="let col of columns">
                {{col.header}}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product let-columns="columns">
        <tr>
            <td>{{product.code}}</td>
            <td *ngFor="let col of columns">
                {{product[col.field]}}
            </td>
        </tr>
    </ng-template>
</p-table>

Table can export its data in CSV format using the built-in exportCSV() function. By default, all data is exported. If you'd like to export only the selection then pass a config object with selectionOnly property as true. Note that columns should be dynamic for export functionality to work, and column objects must define field/header properties.

PDF and EXCEL export are also available using 3rd party libraries such as jspdf. Example below demonstrates how to implement all three export options.

Code Name Category Quantity
f230fh0g3 Bamboo Watch Accessories 24
nvklal433 Black Watch Accessories 61
zz21cz3c1 Blue Band Fitness 2
244wgerg2 Blue T-Shirt Clothing 25
h456wer53 Bracelet Accessories 73


<p-table #dt [columns]="cols" [value]="products" selectionMode="multiple" [(selection)]="selectedProducts" [exportHeader]="'customExportHeader'" [tableStyle]="{ 'min-width': '50rem' }">
    <ng-template pTemplate="caption">
        <div class="flex">
            <button type="button" pButton pRipple icon="pi pi-file" (click)="dt.exportCSV()" class="mr-2" pTooltip="CSV" tooltipPosition="bottom"></button>
            <button type="button" pButton pRipple icon="pi pi-file-excel" (click)="exportExcel()" class="p-button-success mr-2" pTooltip="XLS" tooltipPosition="bottom"></button>
            <button type="button" pButton pRipple icon="pi pi-file-pdf" (click)="exportPdf()" class="p-button-warning mr-2" pTooltip="PDF" tooltipPosition="bottom"></button>
            <button type="button" pButton pRipple icon="pi pi-filter" (click)="dt.exportCSV({ selectionOnly: true })" class="p-button-info ml-auto" pTooltip="Selection Only" tooltipPosition="bottom"></button>
        </div>
    </ng-template>
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns">
                {{ col.header }}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns">
        <tr [pSelectableRow]="rowData">
            <td *ngFor="let col of columns">
                {{ rowData[col.field] }}
            </td>
        </tr>
    </ng-template>
</p-table>

Table has exclusive integration with contextmenu component. In order to attach a menu to a table, add pContextMenuRow directive to the rows that can be selected with context menu, define a local template variable for the menu and bind it to the contextMenu property of the table. This enables displaying the menu whenever a row is right clicked. Optional pContextMenuRowIndex property is available to access the row index. A separate contextMenuSelection property is used to get a hold of the right clicked row. For dynamic columns, setting pContextMenuRowDisabled property as true disables context menu for that particular row.

CodeNameCategoryPrice
f230fh0g3Bamboo WatchAccessories$65.00
nvklal433Black WatchAccessories$72.00
zz21cz3c1Blue BandFitness$79.00
244wgerg2Blue T-ShirtClothing$29.00
h456wer53BraceletAccessories$15.00
av2231fwgBrown PurseAccessories$120.00
bib36pfvmChakra BraceletAccessories$32.00
mbvjkgip5Galaxy EarringsAccessories$34.00
vbb124btrGame ControllerElectronics$99.00
cm230f032Gaming SetElectronics$299.00


<p-contextMenu #cm [model]="items"></p-contextMenu>
<p-table [value]="products" [(contextMenuSelection)]="selectedProduct" [contextMenu]="cm" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header">
        <tr>
            <th>Code</th>
            <th>Name</th>
            <th>Category</th>
            <th>Price</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-product>
        <tr [pContextMenuRow]="product">
            <td>{{product.code}}</td>
            <td>{{product.name}}</td>
            <td>{{product.category}}</td>
            <td>{{product.price | currency: 'USD'}}</td>
        </tr>
    </ng-template>
</p-table>

Stateful table allows keeping the state such as page, sort and filtering either at local storage or session storage so that when the page is visited again, table would render the data using the last settings.

Change the state of the table e.g paginate, navigate away and then return to this table again to test this feature, the setting is set as session with the stateStorage property so that Table retains the state until the browser is closed. Other alternative is local referring to localStorage for an extended lifetime.

Name Country Representative Status
Name James Butt CountryAlgeriaRepresentativeIoni BowcherIoni BowcherStatusunqualified
Name Josephine Darakjy CountryEgyptRepresentativeAmy ElsnerAmy ElsnerStatusproposal
Name Art Venere CountryPanamaRepresentativeAsiya JavayantAsiya JavayantStatusqualified
Name Lenna Paprocki CountrySloveniaRepresentativeXuxue FengXuxue FengStatusnew
Name Donette Foller CountrySouth AfricaRepresentativeAsiya JavayantAsiya JavayantStatusproposal


<p-table #dt1 [value]="customers" selectionMode="single" [(selection)]="selectedCustomers" dataKey="id" [tableStyle]="{'min-width': '50rem'}"
    [rows]="10" [paginator]="true" stateStorage="session" stateKey="statedemo-session">
    <ng-template pTemplate="header">
        <tr>
            <th pSortableColumn="name" style="width:25%">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th pSortableColumn="country.name" style="width:25%">Country <p-sortIcon field="country.name"></p-sortIcon></th>
            <th pSortableColumn="representative.name" style="width:25%">Representative <p-sortIcon field="representative.name"></p-sortIcon></th>
            <th pSortableColumn="status" style="width:25%">Status <p-sortIcon field="status"></p-sortIcon></th>
        </tr>
        <tr>
            <th>
                <input pInputText type="text" (input)="dt1.filter($event.target.value, 'name', 'contains')" [value]="dt1.filters['name']?.value" placeholder="Search by Name" class="w-full">
            </th>
            <th>
                <input pInputText type="text" (input)="dt1.filter($event.target.value, 'country.name', 'contains')" [value]="dt1.filters['country.name']?.value" placeholder="Search by Country" class="w-full">
            </th>
            <th>
                <input pInputText type="text" (input)="dt1.filter($event.target.value, 'representative.name', 'contains')" [value]="dt1.filters['representative.name']?.value" placeholder="Search by Representative" class="w-full">
            </th>
            <th>
                <input pInputText type="text" (input)="dt1.filter($event.target.value, 'status', 'contains')" [value]="dt1.filters['status']?.value" placeholder="Search by Status" class="w-full">
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr [pSelectableRow]="customer">
            <td>
                <span class="p-column-title">Name</span>
                {{customer.name}}
            </td>
            <td>
                <span class="p-column-title">Country</span>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px">
                <span class="ml-1 vertical-align-middle">{{customer.country.name}}</span>
            </td>
            <td>
                <span class="p-column-title">Representative</span>
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{customer.representative.image}}" width="32" style="vertical-align: middle" />
                <span class="ml-1 vertical-align-middle">{{customer.representative.name}}</span>
            </td>
            <td>
                <span class="p-column-title">Status</span>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="emptymessage">
        <tr>
            <td colspan="4">No customers found.</td>
        </tr>
    </ng-template>
</p-table>

DataTable with selection, pagination, filtering, sorting and templating.

List of Customers
Name
Country
Agent
Date
Balance
Status
Activity
Name James Butt CountryAlgeriaRepresentativeIoni BowcherIoni BowcherDate 09/13/2015 Balance $70,663.00 StatusunqualifiedActivity
Name Josephine Darakjy CountryEgyptRepresentativeAmy ElsnerAmy ElsnerDate 02/09/2019 Balance $82,429.00 StatusproposalActivity
Name Art Venere CountryPanamaRepresentativeAsiya JavayantAsiya JavayantDate 05/13/2017 Balance $28,334.00 StatusqualifiedActivity
Name Lenna Paprocki CountrySloveniaRepresentativeXuxue FengXuxue FengDate 09/15/2020 Balance $88,521.00 StatusnewActivity
Name Donette Foller CountrySouth AfricaRepresentativeAsiya JavayantAsiya JavayantDate 05/20/2016 Balance $93,905.00 StatusproposalActivity
Name Simona Morasca CountryEgyptRepresentativeIvan MagalhaesIvan MagalhaesDate 02/16/2018 Balance $50,041.00 StatusqualifiedActivity
Name Mitsue Tollner CountryParaguayRepresentativeIvan MagalhaesIvan MagalhaesDate 02/19/2018 Balance $58,706.00 StatusrenewalActivity
Name Leota Dilliard CountrySerbiaRepresentativeOnyama LimbaOnyama LimbaDate 08/13/2019 Balance $26,640.00 StatusrenewalActivity
Name Sage Wieser CountryEgyptRepresentativeIvan MagalhaesIvan MagalhaesDate 11/21/2018 Balance $65,369.00 StatusunqualifiedActivity
Name Kris Marrier CountryMexicoRepresentativeOnyama LimbaOnyama LimbaDate 07/07/2015 Balance $63,451.00 StatusproposalActivity
Showing 1 to 10 of 200 entries
10


<p-table
    #dt
    [value]="customers"
    [(selection)]="selectedCustomers"
    dataKey="id"
    [rowHover]="true"
    [rows]="10"
    [showCurrentPageReport]="true"
    [rowsPerPageOptions]="[10, 25, 50]"
    [loading]="loading"
    [paginator]="true"
    currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
    [filterDelay]="0"
    [globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
>
    <ng-template pTemplate="caption">
        <div class="table-header">
            List of Customers
            <span class="p-input-icon-left">
                <i class="pi pi-search"></i>
                <input pInputText type="text" (input)="dt.filterGlobal($event.target.value, 'contains')" placeholder="Global Search" />
            </span>
        </div>
    </ng-template>
    <ng-template pTemplate="header">
        <tr>
            <th style="width: 4rem">
                <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
            </th>
            <th pSortableColumn="name" style="min-width: 14rem">
                <div class="flex justify-content-between align-items-center">
                    Name
                    <p-sortIcon field="name"></p-sortIcon>
                    <p-columnFilter type="text" field="name" display="menu" class="ml-auto"></p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="country.name" style="min-width: 14rem">
                <div class="flex justify-content-between align-items-center">
                    Country
                    <p-sortIcon field="country.name"></p-sortIcon>
                    <p-columnFilter type="text" field="country.name" display="menu" class="ml-auto"></p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="representative.name" style="min-width: 14rem">
                <div class="flex justify-content-between align-items-center">
                    Agent
                    <p-sortIcon field="representative.name"></p-sortIcon>
                    <p-columnFilter field="representative" matchMode="in" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false" class="ml-auto">
                        <ng-template pTemplate="header">
                            <div class="px-3 pt-3 pb-0">
                                <span class="font-bold">Agent Picker</span>
                            </div>
                        </ng-template>
                        <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                            <p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name">
                                <ng-template let-option pTemplate="item">
                                    <div class="inline-block vertical-align-middle">
                                        <img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
                                        <span class="ml-1 mt-1">{{ option.name }}</span>
                                    </div>
                                </ng-template>
                            </p-multiSelect>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="date" style="min-width: 10rem">
                <div class="flex justify-content-between align-items-center">
                    Date
                    <p-sortIcon field="date"></p-sortIcon>
                    <p-columnFilter type="date" field="date" display="menu" class="ml-auto"></p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="balance" style="min-width: 10rem">
                <div class="flex justify-content-between align-items-center">
                    Balance
                    <p-sortIcon field="balance"></p-sortIcon>
                    <p-columnFilter type="numeric" field="balance" display="menu" currency="USD" class="ml-auto"></p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="status" style="min-width: 10rem">
                <div class="flex justify-content-between align-items-center">
                    Status
                    <p-sortIcon field="status"></p-sortIcon>
                    <p-columnFilter field="status" matchMode="equals" display="menu" class="ml-auto">
                        <ng-template pTemplate="filter" let-value let-filter="filterCallback">
                            <p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any">
                                <ng-template let-option pTemplate="item">
                                    <p-tag [value]="option.label" [severity]="getSeverity(option.label)"></p-tag>
                                </ng-template>
                            </p-dropdown>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th pSortableColumn="activity" style="min-width: 10rem">
                <div class="flex justify-content-between align-items-center">
                    Activity
                    <p-sortIcon field="activity"></p-sortIcon>
                    <p-columnFilter field="activity" matchMode="between" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false" class="ml-auto">
                        <ng-template pTemplate="filter" let-filter="filterCallback">
                            <p-slider [ngModel]="activityValues" [range]="true" (onSlideEnd)="filter($event.values)" styleClass="m-3"></p-slider>
                            <div class="flex align-items-center justify-content-between px-2">
                                <span>{{ activityValues[0] }}</span>
                                <span>{{ activityValues[1] }}</span>
                            </div>
                        </ng-template>
                    </p-columnFilter>
                </div>
            </th>
            <th style="width: 5rem"></th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-customer>
        <tr class="p-selectable-row">
            <td>
                <p-tableCheckbox [value]="customer"></p-tableCheckbox>
            </td>
            <td>
                <span class="p-column-title">Name</span>
                {{ customer.name }}
            </td>
            <td>
                <span class="p-column-title">Country</span>
                <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
                <span class="ml-1 vertical-align-middle">{{ customer.country.name }}</span>
            </td>
            <td>
                <span class="p-column-title">Representative</span>
                <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
                <span class="ml-1 vertical-align-middle">{{ customer.representative.name }}</span>
            </td>
            <td>
                <span class="p-column-title">Date</span>
                {{ customer.date | date: 'MM/dd/yyyy' }}
            </td>
            <td>
                <span class="p-column-title">Balance</span>
                {{ customer.balance | currency: 'USD':'symbol' }}
            </td>
            <td>
                <span class="p-column-title">Status</span>
                <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
            </td>
            <td>
                <span class="p-column-title">Activity</span>
                <p-progressBar [value]="customer.activity" [showValue]="false"></p-progressBar>
            </td>
            <td style="text-align: center">
                <button pButton type="button" class="p-button-secondary" icon="pi pi-cog"></button>
            </td>
        </tr>
    </ng-template>
    <ng-template pTemplate="emptymessage">
        <tr>
            <td colspan="8">No customers found.</td>
        </tr>
    </ng-template>
</p-table>

CRUD implementation example with a Dialog.

Manage Products
Name ImagePrice Category Reviews Status
Bamboo WatchBamboo Watch$65.00Accessories
INSTOCK
Black WatchBlack Watch$72.00Accessories
INSTOCK
Blue BandBlue Band$79.00Fitness
LOWSTOCK
Blue T-ShirtBlue T-Shirt$29.00Clothing