I have created a Custom LWC Datatable following Creating Custom Data Types in Salesforce Documentation.

My Custom LWC cell component as below,

    <div class="slds-grid slds-cell-wrap slds-align_absolute-center">
        <div class="slds-col">
            <lightning-button label="click" ></lightning-button>
            <lightning-button label="click" onclick={handlePayNow} ></lightning-button>
            <a href="#" onclick={handlePayNow}><b>Pay Now</b></a> | <a href="#" onclick={handleDownload}>Download</a>

and it's Javascript Controller as below,

import { LightningElement, track, api } from 'lwc';

export default class invoiceCustomAction extends LightningElement {


        console.log('test A');
        //console.log('value '+ this.value);


        console.log('test B');
        //console.log('download '+ this.value);


My Custom Data table component as below:


enter image description here

JS Controller

import LightningDatatable from 'lightning/datatable';
import invoiceCustomAction from './invoiceCustomAction.html';

export default class test_InvoicesCustomDatatable extends LightningDatatable {
   static customTypes = {
       customInvoiceCell: {
           template: invoiceCustomAction,
           standardCellLayout: true
           // Provide template data here if needed
      //more custom types here

Consumer code:

component markup:

    <div class = "slds-size_1-of-1 slds-box slds-var-m-around_small">
        <lightning-layout multiple-rows="true">
            <lightning-layout-item size = "12">
                <div  class="slds-size_1-of-1">

JS Controller

import { LightningElement,track,api } from 'lwc';
import FORM_FACTOR from '@salesforce/client/formFactor';
import getInvoices from '@salesforce/apex/Test_InvoiceListController.getInvoices';

const columns = [
    { label: 'Date issued', fieldName: 'issueDate', type: 'date', sortable : true },
    { label: 'Account', fieldName: 'accountNo' , sortable : true, type: 'text'},
    { label: 'Invoice #', fieldName: 'invoiceNo', sortable : true, type: 'text'},
    { label: 'Date due by', fieldName: 'dueDate', type: 'date', sortable : true },
    { label: 'Amount', fieldName: 'amount', type: 'currency', sortable : true },
    { label: 'Status', fieldName: 'status', sortable : true, type: 'text'},
    { label: 'Action', type: 'customInvoiceCell', fieldName: 'index'}

export default class Test_InvoiceList_Outstanding extends LightningElement {

    @track idw = {listIW:[]};                                   // Invoices Display Wrapper Object
    @track page = 1;                                            // Current Page number
    @api perpage = 10;                                          // Records per Page
    @track pages = [];                                          // Page numbers array
    set_size = 3;                                               // Number of Page number buttons
    @track numberOfPages = 1;                                   // Number of pages to show
    @track sortedBy = 'accountNo';                              // Sort field
    @track sortDirection = 'asc';                               // Sort direction
    @track isLoading = true;                                    // Loading UI
    @track searchText = '';                                     // Search text
    @track filteredResults = []                                 // Filtered Invoice wrappers displayed on Datatable,etc.
    @track pageStartIdx = 0;                                    // Page start record index
    @track pageEndIdx = 0;                                      // Page end record index
    @track searchTextPlaceHolder = 'Search by keyword, name';   // Search Text Place holder
    @track selectedRows = [];                                    // Current page's row selection
    @track allSelectedRows = new Map();                          // PageNo => Selected row Ids[] map to keep persistent selection in paginated data table   
    mapPage2SelIdx = new Map()                                    
    navRefresh = false;
        this.columns = columns;
    renderButtons = ()=>{
            but.style.backgroundColor = this.page===parseInt(but.dataset.id,10)?'blue':'white';
    get pagesList(){
        let mid = Math.floor(this.set_size/2) + 1 ;
        if(this.page > mid){
            if (this.page==(this.pages.length))       // Last page
                return this.pages.slice(this.page-mid-1, this.page+mid-1);
                return this.pages.slice(this.page-mid, this.page+mid-1);
        return this.pages.slice(0,this.set_size);
     async connectedCallback(){
        this.isLoading = true;

        this.idw = await getInvoices();
        if (this.idw!=null && this.idw.listIW!=null)
            this.filteredResults = this.idw.listIW.slice(0);
        this.isLoading = false;

    pageData = ()=>{
        let page = this.page;
        let perpage = this.perpage;
        let startIndex = (page*perpage) - perpage;
        let endIndex = (page*perpage);
        this.pageStartIdx = startIndex + 1;
        this.pageEndIdx = (endIndex > this.filteredResults.length) ? this.filteredResults.length : endIndex;

        return this.filteredResults.slice(startIndex,endIndex);

    setPages = (data)=>{
        this.page = 1;
        this.pages = [];
        this.numberOfPages = Math.ceil(data.length / this.perpage);
        for (let index = 1; index <= this.numberOfPages; index++) {
    get hasPrev(){
        return this.page > 1;
    get hasNext(){
        return this.page < this.pages.length

    onNext = ()=>{
        this.navRefresh = true;

    onPrev = ()=>{
        this.navRefresh = true;

    onPageClick = (e)=>{
        this.navRefresh = true;
        this.page = parseInt(e.target.dataset.id,10);

    get currentPageData(){
        this.selectedRows = this.selectedRows.splice(0);
        return this.pageData();

    get currentSelection(){
        let selectedRecs = this.mapPage2SelIdx.get(this.page);
        if (!Array.isArray(selectedRecs))
            selectedRecs = [];
        return selectedRecs;

    updateColumnSorting(event) {       
        this.sortedBy = event.detail.fieldName;
        this.sortDirection = event.detail.sortDirection;


        var data = this.filteredResults;
        var reverse = this.sortDirection !== 'asc';
        data.sort(this.sortBy(sortedBy, reverse));
        this.filteredResults = data;

    sortBy(field, reverse, primer) {
        var key = primer ?
            function(x) {return primer(x[field])} :
            function(x) {return x[field]};
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            return a = key(a), b = key(b), reverse * ((a > b) - (b > a));

    handleFilterChange(event) {

On the custom datatable component cell, when you click any button/url except the one having event handler, it's throwing the below error.

enter image description here

Any help to understand what it means? what could be missing in the custom LWC component? Thanks!

  • 1
    As such there is nothing wrong in your LWC cell component code. This is a generic JS error that can occur for a variety of reasons. Can you provide the source code of the custom datatable component and the consumer component?
    – arut
    Aug 31, 2020 at 9:44
  • Hi @arut Thank you for your reply. I have added more code to explain. Cheers! Aug 31, 2020 at 23:43
  • 1
    I'm unable to replicate the issue. When I click on unhandled button (first button element), nothing happens as expected and when I click on other button/ URLs, the expected handler is executing the console.log methods. BTW, in invoiceCustomAction.html, I believe you have the only the following code: <template><c-invoice-custom-action></c-invoice-custom-action></template>. Let me know, if otherwise.
    – arut
    Sep 1, 2020 at 8:18
  • I have used your code with a minor modification in connectedCallback (i.e., getInvoices call). In my case, I have hardcoded data in the JS file and replaced the apex call with this data. Why are you not using wired apex (instead of making call from within connectedCallback)? Marking this lifecycle hook as async means interrupting the standard component life cycle flow. Have you tried placing few console.log statements inside this connectedCallback and observe the values? I suspect something is wrong here causing downstream impact, but unable to confirm without the apex source code.
    – arut
    Sep 1, 2020 at 8:31
  • 1
    I had the cell component named as invoiceCustomAction and also the html file inside test_InvoicesCustomDatatable named as invoiceCustomAction.html. So, in my previous comment, I had intended to say that a HTML file should be present in the custom data table component that references the cell component (it basically serves as a conduit). I didn't realize that about your code dir structure. And, you are right about the HTML markup only not requiring a separate component. This is most likely for any static image or text kind of display scenario (which doesn't require any user interaction).
    – arut
    Sep 2, 2020 at 8:07

2 Answers 2


In the custom data table implementation code (i.e., the component that extends LightningDatatable/ in your case test_InvoicesCustomDatatable), a reference should be provided to either another custom component or an HTML file. This custom component or HTML file is the one that defines the custom cell structure (& related code) to be used in the custom data table.

Case 1: If the custom cell displays static content and doesn't implement any custom user interaction, then define a custom HTML file and place it within the custom data table component directory [i.e., test_InvoicesCustomDatatable will have test_InvoicesCustomDatatable.html as well as another HTML file (say customCellContent.html) that defines cell content]. Now, in the component JS file, you can import HTML file using the code import customCellContent from './customCellContent.html' and use this import to define the custom data types. Sample code as shown below:

static customTypes = {
    customDataType: {
        template: customCellContent,
        standardCellLayout: true,
        // Provide template data here if needed
        typeAttributes: ['attrA', 'attrB'] // Define custom attributes, if needed

Case 2: If the custom cell has complex structure or implements custom user interactions (such as buttons etc.), then create a new LWC component (say myCustomCellComp) and place all the cell related HTML/ code logic in the same. Now, create a HTML file (customCellContent.HTML) within the custom datatable component directory (similar to previous case) and place the following code in it : <template><c-my-custom-cell-comp></c-my-custom-cell-comp></template>. This HTML file will serve as a conduit between the cell component and the datatable component. Importing the HTML reference and defining custom data type in the JS file will remain the same as previous case.

So, depending on the requirement, you might need to create either 2 or 1 lightning web components and any consumer lwc can make use of the custom data table component. For more details, refer SF official documentation.


The error is not descriptive enough because you have disabled Lightning Components Debug Mode for the current user.

  1. Go to Setup
  2. In quick find type 'Debug Mode'
  3. Click on the Debug Mode menu under Lightning Components
  4. Select the user you wish to enable Debug mode for and click enabled.
  5. Head back to your component and this time you should see a more descriptive error on the Modal.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .