A common way to display data is to use datatables. LWC provides the lightning-datatable base component for displaying large amounts of data. lightning-datatable supports a wide range of column types that are ideal for displaying record data, such as currency, date, email, and even a dropdown menu for row-level actions. You can also load more data by implementing pagination or infinite scrolling on the datatable.
To display your records using lightning-datatable, set key-field="Id" and specify the data and column properties.
<!-- datatableWithGraphql.html -->
<template>
<template lwc:if={accounts}>
<lightning-datatable key-field="Id" data={accounts} columns={columns} hide-checkbox-column>
</lightning-datatable>
</template>
<template lwc:elseif={errors}>
<!-- Display errors -->
</template>
</template>
In your JavaScript, create your columns and define your data using key-value pairs when it’s returned by the GraphQL wire adapter.
// datatableWithGraphql.js
import { LightningElement, wire } from "lwc";
import { gql, graphql } from "lightning/graphql";
const columns = [
{ label: "Name", fieldName: "Name" },
{ label: "Phone", fieldName: "Phone", type: "phone" },
{ label: "Website", fieldName: "Website", type: "url" },
{ label: "Annual Revenue", fieldName: "AnnualRevenue", type: "currency" },
];
export default class DatatableWithGraphql extends LightningElement {
// Array of accounts to display
accounts = undefined;
// Errors from the wire adapter
errors = undefined;
// Columns for datatable
columns = columns;
@wire(graphql, {
query: gql`
query AccountWithName {
uiapi {
query {
Account {
edges {
node {
Id
Name {
value
}
Phone {
value
}
Website {
value
}
AnnualRevenue {
displayValue
}
}
}
}
}
}
}
`,
})
gqlQuery({ data, errors }) {
if (data) {
this.accounts = data.uiapi.query.Account.edges.map((edge) => ({
Id: edge.node.Id,
Name: edge.node.Name.value,
Phone: edge.node.Phone.value,
Website: edge.node.Website.value,
AnnualRevenue: edge.node.AnnualRevenue.displayValue,
}));
}
this.errors = errors;
}
}
Add pagination to the datatable with a refresh button and a next button. The refresh button takes users back to the start of the results. The next button becomes disabled when the end of the results is reached.
note: Backward pagination using last and before isn’t currently supported.
<!-- datatableWithGraphqlPagination -->
<template>
<lightning-button-icon icon-name="utility:skip_back" onclick="{resetPagingCursor}"
><label>Reset Pagination</label></lightning-button-icon
>
<lightning-button-icon
disabled="{isFinalPage}"
class="next"
icon-name="utility:chevronright"
onclick="{getNextPage}"
><label>Next</label></lightning-button-icon
>
<p>Total records found: {totalCount}</p>
<p>Page {currentPage} of {totalPages}</p>
<template lwc:if="{accounts}">
<lightning-datatable key-field="Id" data="{accounts}" columns="{columns}" hide-checkbox-column>
</lightning-datatable>
</template>
<template lwc:elseif="{errors}">
<!-- Display errors -->
</template>
</template>
Building on the previous lightning-datatable example, query the pageInfo object for cursor and pagination information.
// datatableWithGraphqlPagination.js
import { LightningElement, wire } from "lwc";
import { gql, graphql } from "lightning/graphql";
const columns = [
{ label: "Name", fieldName: "Name" },
{ label: "Phone", fieldName: "Phone", type: "phone" },
{ label: "Website", fieldName: "Website", type: "url" },
{ label: "Annual Revenue", fieldName: "AnnualRevenue", type: "currency" },
];
export default class DatatableWithGraphqlPagination extends LightningElement {
// Array of accounts to display
accounts = undefined;
// Errors from the wire adapter
errors = undefined;
// Columns for datatable
columns = columns;
// Cursor of the last returned record
lastCursor = null;
// Cursor for the next page
endCursor = null;
// Checks if the page is the last page
// Disables the next page button if true
isFinalPage = false;
// Total number of query results
totalCount = null;
page = 1;
totalPages = 1;
@wire(graphql, {
query: "$accountQuery",
variables: "$variables",
})
gqlQuery({ data, errors }) {
if (data) {
this.accounts = data.uiapi.query.Account.edges.map((edge) => ({
Id: edge.node.Id,
Name: edge.node.Name.value,
Phone: edge.node.Phone.value,
Website: edge.node.Website.value,
AnnualRevenue: edge.node.AnnualRevenue.displayValue,
}));
this.isFinalPage = !data.uiapi.query.Account.pageInfo.hasNextPage;
this.endCursor = data.uiapi.query.Account.pageInfo.endCursor;
this.totalCount = data.uiapi.query.Account.totalCount;
this.totalPages = Math.ceil(this.totalCount / 10);
}
this.errors = errors;
}
// Define the GraphQL query
get accountQuery() {
return gql`
query AccountWithName($after: String) {
uiapi {
query {
Account(after: $after) {
edges {
node {
Id
Name {
value
}
Phone {
value
}
Website {
value
}
AnnualRevenue {
displayValue
}
}
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
}
totalCount
}
}
}
}
`;
}
// Define variables for the GraphQL query
get variables() {
return {
after: this.lastCursor,
};
}
get currentPage() {
return this.totalCount === 0 ? 0 : this.page;
}
// Click handler for the reset button
resetPagingCursor(event) {
this.lastCursor = null;
this.page = 1;
}
// Click handler for the next page button
getNextPage(event) {
if (!this.isFinalPage) {
this.lastCursor = this.endCursor;
this.page++;
}
}
}