Datatables

Bootstrap 5 Datatables

The Datatable is a component which mix tables with advanced options like searching, sorting and pagination.

Note: Read the API tab to find all available options and advanced customization

Video tutorial


Basic example - HTML markup

The Datatable component can render your data in three ways. In the first one, you simply create a HTML markup for your table nested within a div tag with a "datatable" class - you can customize your table later by adding data-mdb-attributes to the wrapper. Some of the more advanced options for columns, described in the Advanced Data Structure section can be also used by setting data-mdb-attributes directly to a th tag (f.e. <th data-mdb-sort="false">).

Datatable collects information from HTML markup to create a data structure - the <table> element will be replaced in the DOM with a different node after component initializes.

Name Position Office Age Start date Salary
Tiger Nixon System Architect Edinburgh 61 2011/04/25 $320,800
Garrett Winters Accountant Tokyo 63 2011/07/25 $170,750
Ashton Cox Junior Technical Author San Francisco 66 2009/01/12 $86,000
Cedric Kelly Senior Javascript Developer Edinburgh 22 2012/03/29 $433,060
Airi Satou Accountant Tokyo 33 2008/11/28 $162,700
Brielle Williamson Integration Specialist New York 61 2012/12/02 $372,000
Herrod Chandler Sales Assistant San Francisco 59 2012/08/06 $137,500
Rhona Davidson Integration Specialist Tokyo 55 2010/10/14 $327,900
Colleen Hurst Javascript Developer San Francisco 39 2009/09/15 $205,500
Sonya Frost Software Engineer Edinburgh 23 2008/12/13 $103,600
Jena Gaines Office Manager London 30 2008/12/19 $90,560
Quinn Flynn Support Lead Edinburgh 22 2013/03/03 $342,000
Charde Marshall Regional Director San Francisco 36 2008/10/16 $470,600
Haley Kennedy Senior Marketing Designer London 43 2012/12/18 $313,500
        
            
          <div  class="datatable">
            <table>
              <thead>
                <tr>
                  <th class="th-sm">Name</th>
                  <th class="th-sm">Position</th>
                  <th class="th-sm">Office</th>
                  <th class="th-sm">Age</th>
                  <th class="th-sm">Start date</th>
                  <th class="th-sm">Salary</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>Tiger Nixon</td>
                  <td>System Architect</td>
                  <td>Edinburgh</td>
                  <td>61</td>
                  <td>2011/04/25</td>
                  <td>$320,800</td>
                </tr>
                <tr>
                  <td>Garrett Winters</td>
                  <td>Accountant</td>
                  <td>Tokyo</td>
                  <td>63</td>
                  <td>2011/07/25</td>
                  <td>$170,750</td>
                </tr>
                <tr>
                  <td>Ashton Cox</td>
                  <td>Junior Technical Author</td>
                  <td>San Francisco</td>
                  <td>66</td>
                  <td>2009/01/12</td>
                  <td>$86,000</td>
                </tr>
                <tr>
                  <td>Cedric Kelly</td>
                  <td>Senior Javascript Developer</td>
                  <td>Edinburgh</td>
                  <td>22</td>
                  <td>2012/03/29</td>
                  <td>$433,060</td>
                </tr>
                <tr>
                  <td>Airi Satou</td>
                  <td>Accountant</td>
                  <td>Tokyo</td>
                  <td>33</td>
                  <td>2008/11/28</td>
                  <td>$162,700</td>
                </tr>
                <tr>
                  <td>Brielle Williamson</td>
                  <td>Integration Specialist</td>
                  <td>New York</td>
                  <td>61</td>
                  <td>2012/12/02</td>
                  <td>$372,000</td>
                </tr>
                <tr>
                  <td>Herrod Chandler</td>
                  <td>Sales Assistant</td>
                  <td>San Francisco</td>
                  <td>59</td>
                  <td>2012/08/06</td>
                  <td>$137,500</td>
                </tr>
                <tr>
                  <td>Rhona Davidson</td>
                  <td>Integration Specialist</td>
                  <td>Tokyo</td>
                  <td>55</td>
                  <td>2010/10/14</td>
                  <td>$327,900</td>
                </tr>
                <tr>
                  <td>Colleen Hurst</td>
                  <td>Javascript Developer</td>
                  <td>San Francisco</td>
                  <td>39</td>
                  <td>2009/09/15</td>
                  <td>$205,500</td>
                </tr>
                <tr>
                  <td>Sonya Frost</td>
                  <td>Software Engineer</td>
                  <td>Edinburgh</td>
                  <td>23</td>
                  <td>2008/12/13</td>
                  <td>$103,600</td>
                </tr>
                <tr>
                  <td>Jena Gaines</td>
                  <td>Office Manager</td>
                  <td>London</td>
                  <td>30</td>
                  <td>2008/12/19</td>
                  <td>$90,560</td>
                </tr>
                <tr>
                  <td>Quinn Flynn</td>
                  <td>Support Lead</td>
                  <td>Edinburgh</td>
                  <td>22</td>
                  <td>2013/03/03</td>
                  <td>$342,000</td>
                </tr>
                <tr>
                  <td>Charde Marshall</td>
                  <td>Regional Director</td>
                  <td>San Francisco</td>
                  <td>36</td>
                  <td>2008/10/16</td>
                  <td>$470,600</td>
                </tr>
                <tr>
                  <td>Haley Kennedy</td>
                  <td>Senior Marketing Designer</td>
                  <td>London</td>
                  <td>43</td>
                  <td>2012/12/18</td>
                  <td>$313,500</td>
                </tr>
              </tbody>
            </table>
          </div>
        
        
    

Basic data structure

The second option is a very basic data structure, where columns are represented by an array of strings and so is each row. The table will match each string in a row to a corresponding index in a columns array. This data structure, as it's based on indexes, not key-value pairs, can be easily used for displaying data from the CSV format.

        
            
          <div id="datatable"></div>
        
        
    
        
            
          const basicData = {
            columns: ['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary'],
            rows: [
              ["Tiger Nixon", "System Architect", "Edinburgh", "61", "2011/04/25", "$320,800"],
              ["Garrett Winters", "Accountant", "Tokyo", "63", "2011/07/25", "$170,750"],
              ["Ashton Cox", "Junior Technical Author", "San Francisco", "66", "2009/01/12", "$86,000"],
              ["Cedric Kelly", "Senior Javascript Developer", "Edinburgh", "22", "2012/03/29", "$433,060"],
              ["Airi Satou", "Accountant", "Tokyo", "33", "2008/11/28", "$162,700"],
              ["Brielle Williamson", "Integration Specialist", "New York", "61", "2012/12/02", "$372,000"],
              ["Herrod Chandler", "Sales Assistant", "San Francisco", "59", "2012/08/06", "$137,500"],
              ["Rhona Davidson", "Integration Specialist", "Tokyo", "55", "2010/10/14", "$327,900"],
              ["Colleen Hurst", "Javascript Developer", "San Francisco", "39", "2009/09/15", "$205,500"],
              ["Sonya Frost", "Software Engineer", "Edinburgh", "23", "2008/12/13", "$103,600"],
              ["Jena Gaines", "Office Manager", "London", "30", "2008/12/19", "$90,560"],
              ["Quinn Flynn", "Support Lead", "Edinburgh", "22", "2013/03/03", "$342,000"],
              ["Charde Marshall", "Regional Director", "San Francisco", "36", "2008/10/16", "$470,600"],
              ["Haley Kennedy", "Senior Marketing Designer", "London", "43", "2012/12/18", "$313,500"]
            ],
          };

          new mdb.Datatable(document.getElementById('datatable'), basicData)
        
        
    

Advanced data structure

The last and most advanced data structure allows customizing each column (sort, width, fixed, field) and matches values from each row to a column in which the `field` equals to a given key value. This data format can be easily used to display JSON data.

You can also use a mixed version, where columns are an array of object and each row is an array of strings.

        
            
          <div id="datatable"></div>
        
        
    
        
            
          const advancedData = {
            columns: [
              { label: 'Name', field: 'name', sort: true },
              { label: 'Position', field: 'position', sort: false },
              { label: 'Office', field: 'office', sort: false },
              { label: 'Age', field: 'age', sort: false },
              { label: 'Start date', field: 'date', sort: true },
              { label: 'Salary', field: 'salary', sort: false },
            ],
            rows: [
              { name: "Tiger Nixon", position: "System Architect", office: "Edinburgh", age: 61, date: "2011/04/25", salary: "$320,800" },
              { name: "Garrett Winters", position: "Accountant", office: "Tokyo", age: 63, date: "2011/07/25", salary: "$170,750" },
              { name: "Ashton Cox", position: "Junior Technical Author", office: "San Francisco", age: 66, date: "2009/01/12", salary: "$86,000" },
              { name: "Cedric Kelly", position: "Senior Javascript Developer", office: "Edinburgh", age: 22, date: "2012/03/29", salary: "$433,060" },
              { name: "Airi Satou", position: "Accountant", office: "Tokyo", age: 33, date: "2008/11/28", salary: "$162,700" },
              { name: "Brielle Williamson", position: "Integration Specialist", office: "New York", age: 61, date: "2012/12/02", salary: "$372,000" },
              { name: "Herrod Chandler", position: "Sales Assistant", office: "San Francisco", age: 59, date: "2012/08/06", salary: "$137,500" },
              { name: "Rhona Davidson", position: "Integration Specialist", office: "Tokyo", age: 55, date: "2010/10/14", salary: "$327,900" },
              { name: "Colleen Hurst", position: "Javascript Developer", office: "San Francisco", age: 39, date: "2009/09/15", salary: "$205,500" },
              { name: "Sonya Frost", position: "Software Engineer", office: "Edinburgh", age: 23, date: "2008/12/13", salary: "$103,600" },
              { name: "Jena Gaines", position: "Office Manager", office: "London", age: 30, date: "2008/12/19", salary: "$90,560" },
              { name: "Quinn Flynn", position: "Support Lead", office: "Edinburgh", age: 22, date: "2013/03/03", salary: "$342,000" },
              { name: "Charde Marshall", position: "Regional Director", office: "San Francisco", age: 36, date: "2008/10/16", salary: "$470,600" },
              { name: "Haley Kennedy", position: "Senior Marketing Designer", office: "London", age: 43, date: "2012/12/18", salary: "$313,500" }
            ],
          };

          new mdb.Datatable(document.getElementById('datatable'), advancedData)
        
        
    



Selectable rows

When the selectable option is set to true, user can interact with your table by selecting rows - you can get the selected rows by listening to the selectRows.mdb.datatable event.

        
            
          <div id="datatable" data-mdb-selectable="true" data-mdb-multi="true"></div>
        
        
    
        
            
          const basicData = {
            columns: ['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary'],
            rows: [
              ["Tiger Nixon", "System Architect", "Edinburgh", "61", "2011/04/25", "$320,800"],
              ["Garrett Winters", "Accountant", "Tokyo", "63", "2011/07/25", "$170,750"],
              ["Ashton Cox", "Junior Technical Author", "San Francisco", "66", "2009/01/12", "$86,000"],
              ["Cedric Kelly", "Senior Javascript Developer", "Edinburgh", "22", "2012/03/29", "$433,060"],
              ["Airi Satou", "Accountant", "Tokyo", "33", "2008/11/28", "$162,700"],
              ["Brielle Williamson", "Integration Specialist", "New York", "61", "2012/12/02", "$372,000"],
              ["Herrod Chandler", "Sales Assistant", "San Francisco", "59", "2012/08/06", "$137,500"],
              ["Rhona Davidson", "Integration Specialist", "Tokyo", "55", "2010/10/14", "$327,900"],
              ["Colleen Hurst", "Javascript Developer", "San Francisco", "39", "2009/09/15", "$205,500"],
              ["Sonya Frost", "Software Engineer", "Edinburgh", "23", "2008/12/13", "$103,600"],
              ["Jena Gaines", "Office Manager", "London", "30", "2008/12/19", "$90,560"],
              ["Quinn Flynn", "Support Lead", "Edinburgh", "22", "2013/03/03", "$342,000"],
              ["Charde Marshall", "Regional Director", "San Francisco", "36", "2008/10/16", "$470,600"],
              ["Haley Kennedy", "Senior Marketing Designer", "London", "43", "2012/12/18", "$313,500"]
            ],
          };

          const datatable = document.getElementById('datatable');

          new mdb.Datatable(datatable, basicData);

          datatable.addEventListener('selectRows.mdb.datatable', (e) => {
            console.log(e.selectedRows, e.selectedIndexes, e.allSelected);
          })
        
        
    

Scroll

Setting maximum height/width will enable vertical/horizontal scrolling.

        
            
          <div id="datatable" data-mdb-max-height="520" data-mdb-max-width="520"></div>
        
        
    
        
            
          const basicData = {
            columns: ['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary'],
            rows: [
              ["Tiger Nixon", "System Architect", "Edinburgh", "61", "2011/04/25", "$320,800"],
              ["Garrett Winters", "Accountant", "Tokyo", "63", "2011/07/25", "$170,750"],
              ["Ashton Cox", "Junior Technical Author", "San Francisco", "66", "2009/01/12", "$86,000"],
              ["Cedric Kelly", "Senior Javascript Developer", "Edinburgh", "22", "2012/03/29", "$433,060"],
              ["Airi Satou", "Accountant", "Tokyo", "33", "2008/11/28", "$162,700"],
              ["Brielle Williamson", "Integration Specialist", "New York", "61", "2012/12/02", "$372,000"],
              ["Herrod Chandler", "Sales Assistant", "San Francisco", "59", "2012/08/06", "$137,500"],
              ["Rhona Davidson", "Integration Specialist", "Tokyo", "55", "2010/10/14", "$327,900"],
              ["Colleen Hurst", "Javascript Developer", "San Francisco", "39", "2009/09/15", "$205,500"],
              ["Sonya Frost", "Software Engineer", "Edinburgh", "23", "2008/12/13", "$103,600"],
              ["Jena Gaines", "Office Manager", "London", "30", "2008/12/19", "$90,560"],
              ["Quinn Flynn", "Support Lead", "Edinburgh", "22", "2013/03/03", "$342,000"],
              ["Charde Marshall", "Regional Director", "San Francisco", "36", "2008/10/16", "$470,600"],
              ["Haley Kennedy", "Senior Marketing Designer", "London", "43", "2012/12/18", "$313,500"]
            ],
          };

          const datatable = document.getElementById('datatable');

          new mdb.Datatable(datatable, basicData);
        
        
    

Fixed header

Use the fixedHeader option to ensure that a table's header is always visible while scrolling.

        
            
          <div id="datatable" data-mdb-max-height="460" data-mdb-fixed-header="true"></div>
        
        
    
        
            
          const basicData = {
            columns: ['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary'],
            rows: [
              ["Tiger Nixon", "System Architect", "Edinburgh", "61", "2011/04/25", "$320,800"],
              ["Garrett Winters", "Accountant", "Tokyo", "63", "2011/07/25", "$170,750"],
              ["Ashton Cox", "Junior Technical Author", "San Francisco", "66", "2009/01/12", "$86,000"],
              ["Cedric Kelly", "Senior Javascript Developer", "Edinburgh", "22", "2012/03/29", "$433,060"],
              ["Airi Satou", "Accountant", "Tokyo", "33", "2008/11/28", "$162,700"],
              ["Brielle Williamson", "Integration Specialist", "New York", "61", "2012/12/02", "$372,000"],
              ["Herrod Chandler", "Sales Assistant", "San Francisco", "59", "2012/08/06", "$137,500"],
              ["Rhona Davidson", "Integration Specialist", "Tokyo", "55", "2010/10/14", "$327,900"],
              ["Colleen Hurst", "Javascript Developer", "San Francisco", "39", "2009/09/15", "$205,500"],
              ["Sonya Frost", "Software Engineer", "Edinburgh", "23", "2008/12/13", "$103,600"],
              ["Jena Gaines", "Office Manager", "London", "30", "2008/12/19", "$90,560"],
              ["Quinn Flynn", "Support Lead", "Edinburgh", "22", "2013/03/03", "$342,000"],
              ["Charde Marshall", "Regional Director", "San Francisco", "36", "2008/10/16", "$470,600"],
              ["Haley Kennedy", "Senior Marketing Designer", "London", "43", "2012/12/18", "$313,500"]
            ],
          };

          const datatable = document.getElementById('datatable');

          new mdb.Datatable(datatable, basicData);
        
        
    

Fixed columns

Making a column sticky requires setting two options - width and fixed. A first option is a number of pixels, while the other one can be either a true ( in which case the column will stick on the left) or a string right.

Using fixed columns in a vertically scrollable table, requires setting an option fixedHeader to true as well.

When using a HTML markup instead of a data structure you can still use this feature by setting data-mdb-width and data-mdb-fixed attributes on your th tags.

        
            
          <div id="datatable"></div>
        
        
    
        
            
          const basicData = {
            columns: [
              { label: 'Name', field: 'name', sort: true, width: 200, fixed: true },
              { label: 'Position', field: 'position', sort: false, width: 200 },
              { label: 'Office', field: 'office', sort: false, width: 200, fixed: true },
              { label: 'Age', field: 'age', sort: false, width: 200 },
              { label: 'Start date', field: 'date', sort: true, width: 200 },
              { label: 'Salary', field: 'salary', sort: false, width: 200, fixed: 'right' },
            ],
            rows: [
              ["Tiger Nixon", "System Architect", "Edinburgh", "61", "2011/04/25", "$320,800"],
              ["Garrett Winters", "Accountant", "Tokyo", "63", "2011/07/25", "$170,750"],
              ["Ashton Cox", "Junior Technical Author", "San Francisco", "66", "2009/01/12", "$86,000"],
              ["Cedric Kelly", "Senior Javascript Developer", "Edinburgh", "22", "2012/03/29", "$433,060"],
              ["Airi Satou", "Accountant", "Tokyo", "33", "2008/11/28", "$162,700"],
              ["Brielle Williamson", "Integration Specialist", "New York", "61", "2012/12/02", "$372,000"],
              ["Herrod Chandler", "Sales Assistant", "San Francisco", "59", "2012/08/06", "$137,500"],
              ["Rhona Davidson", "Integration Specialist", "Tokyo", "55", "2010/10/14", "$327,900"],
              ["Colleen Hurst", "Javascript Developer", "San Francisco", "39", "2009/09/15", "$205,500"],
              ["Sonya Frost", "Software Engineer", "Edinburgh", "23", "2008/12/13", "$103,600"],
              ["Jena Gaines", "Office Manager", "London", "30", "2008/12/19", "$90,560"],
              ["Quinn Flynn", "Support Lead", "Edinburgh", "22", "2013/03/03", "$342,000"],
              ["Charde Marshall", "Regional Director", "San Francisco", "36", "2008/10/16", "$470,600"],
              ["Haley Kennedy", "Senior Marketing Designer", "London", "43", "2012/12/18", "$313,500"]
            ],
          };

          const datatable = document.getElementById('datatable');

          new mdb.Datatable(datatable, basicData);
        
        
    

Async data

Loading content asynchronously is an important part of working with data tables - with MDB Datatable you can easily display content after fetching it from API by using the update method. Additionally, setting a loading option to true will disable all interactions and display a simple loader while awaiting data.
The example below demonstrates loading data after the button is pressed.

        
            
          <div id="datatable" data-mdb-loading="true"></div>
        
        
    
        
            
          const columns = [
            { label: 'Address', field: 'address' },
            { label: 'Company', field: 'company' },
            { label: 'Email', field: 'email' },
            { label: 'Name', field: 'name' },
            { label: 'Phone', field: 'phone' },
            { label: 'Username', field: 'username' },
            { label: 'Website', field: 'website' },
          ];

          const asyncTable = new mdb.Datatable(
            document.getElementById('datatable'),
            { columns, },
            { loading: true }
          );

          fetch('https://jsonplaceholder.typicode.com/users')
            .then((response) => response.json())
            .then((data) => {
              asyncTable.update(
                {
                  rows: data.map((user) => ({
                    ...user,
                    address: `${user.address.city}, ${user.address.street}`,
                    company: user.company.name,
                  })),
                },
                { loading: false }
              );
            });

        
        
    

Action buttons

With the Datatable it's possible to render custom content, such as action buttons and attach listeners to their events. Keep in mind, that the component rerenders content when various actions occur (f.e. sort, search) and event listeners need to be updated. To make it possible, the components emits a custom event render.mdb.datatable.

        
            
          <div id="datatable-custom"></div>
        
        
    
        
            
          const customDatatable = document.getElementById('datatable-custom');

          const setActions = () => {
            document.getElementsByClassName('call-btn').forEach(btn => {
              btn.addEventListener('click', () => {
                console.log(`call ${btn.attributes['data-mdb-number'].value}`)
              })
            })

            document.getElementsByClassName('message-btn').forEach(btn => {
              btn.addEventListener('click', () => {
                console.log(`send a message to ${btn.attributes['data-mdb-email'].value}`)
              })
            })
          }

          customDatatable.addEventListener('render.mdb.datatable', setActions);

          new mdb.Datatable(customDatatable, {
            columns: [
              { label: 'Name', field: 'name' },
              { label: 'Position', field: 'position' },
              { label: 'Office', field: 'office' },
              { label: 'Contact', field: 'contact', sort: false },
            ],
            rows: [
              {
                name: 'Tiger Nixon',
                position: 'System Architect',
                office: 'Edinburgh',
                phone: '+48000000000',
                email: 'tiger.nixon@gmail.com'
              },
              {
                name: 'Sonya Frost',
                position: 'Software Engineer',
                office: 'Edinburgh',
                phone: '+53456123456',
                email: 'sfrost@gmail.com'
              },
              {
                name: 'Tatyana Fitzpatrick',
                position: 'Regional Director',
                office: 'London',
                phone: '+42123432456',
                email: 'tfitz@gmail.com'
              },
            ].map((row) => {
              return {
                ...row,
                contact: `
                <button class="call-btn btn btn-outline-primary btn-floating btn-sm" data-mdb-number="${row.phone}"><i class="fa fa-phone"></i></button>
                <button class="message-btn btn ms-2 btn-primary btn-floating btn-sm" data-mdb-email="${row.email}"><i class="fa fa-envelope"></i></button>`,
              };
            }),
          }, { hover: true });
        
        
    

Cell formatting

Use cell formatting to color individual cells.

        
            
          <div id="datatable-cell-format" data-mdb-sort-field="purchases" data-mdb-sort-order="desc"></div>
        
        
    
        
            
          const rows = [
            ['Product 1', 10, 103],
            ['Product 2', 45, 110],
            ['Product 3', 76, 56],
            ['Product 4', 89, 230],
            ['Product 5', 104, 240],
            ['Product 6', 97, 187],
            ['Product 7', 167, 130],
            ['Product 8', 50, 199],
            ['Product 9', 4, 206],
            ['Product 10', 120, 88],
            ['Product 11', 22, 100],
          ];

          const maxValue = Math.max(...rows.map((row) => row[2]));
          const minValue = Math.min(...rows.map((row) => row[2]));

          const colors = ['#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5'];

          const step = (maxValue - minValue) / (colors.length - 1);

          const formatCell = (cell, value) => {
            const colorIndex = Math.floor((value - minValue) / step);

            cell.style.backgroundColor = colors[colorIndex];
            cell.style.fontWeight = 400;
          };

          const columns = [
            { label: 'Product', field: 'product' },
            { label: 'Quantity', field: 'quantity' },
            { label: 'Purchases', field: 'purchases', format: formatCell },
          ];

          const datatableInstance = new mdb.Datatable(
            document.getElementById('datatable-cell-format'),
            { rows, columns }
          );
        
        
    

Clickable rows

Click on the row to preview the message.

Selecting the row with checkbox doesn't trigger rowClick event.

Note: To prevent this action with other clickable elements within the row, call stopPropagation() method.

Note: This feature cannot be used simultaneously with edit option.

        
            
          <div id="datatable-clickable-rows" data-mdb-clickable-rows="true" data-mdb-selectable="true" data-mdb-multi="true"></div>

          <!-- Modal -->
          <div class="modal fade" tabindex="-1" aria-hidden="true" id="modal-clickable-rows">
            <div class="modal-dialog">
              <div class="modal-content">
                <div class="modal-header">
                  <h5 class="modal-title" id="modal-header-clickable-rows"></h5>
                  <button type="button" class="btn-close" data-mdb-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body mt-4 mb-5" id="modal-body-clickable-rows"></div>
                <div class="modal-footer">
                  <button type="button" class="btn btn-outline-primary">
                    Reply
                    <i class="fa fa-paper-plane ms-2"></i>
                  </button>
                  <button type="button" class="btn btn-outline-primary">
                    Forward
                    <i class="fa fa-arrow-right ms-2"></i>
                  </button>
                </div>
              </div>
            </div>
          </div>
        
        
    
        
            
          const table = document.getElementById('datatable-clickable-rows');
          const modal = document.getElementById('modal-clickable-rows');
          const modalBody = document.getElementById('modal-body-clickable-rows');
          const modalHeader = document.getElementById('modal-header-clickable-rows');

          const modalInstance = new mdb.Modal(modal);

          const setupButtons = (action) => {
            document.getElementsByClassName(`${action}-email-button`).forEach((button) => {
              button.addEventListener('click', (e) => {
                e.stopPropagation();

                const index = button.getAttribute('data-mdb-index');

                console.log(`${action} message: ${index}`, messages[index]);
              });
            });
          };

          const columns = [
            { label: 'Actions', field: 'actions', sort: false },
            { label: 'From', field: 'from' },
            { label: 'Title', field: 'title' },
            { label: 'Message', field: 'preview', sort: false },
            { label: 'Date', field: 'date' },
          ];

          const messages = [
            {
              from: 'admin@mdbootstrap.com',
              title: 'MDBootstrap spring sale',
              message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sed metus ultricies, sollicitudin est nec, blandit turpis. Fusce venenatis nisi volutpat, pharetra elit eu, ullamcorper metus. Vestibulum dapibus laoreet aliquam. Maecenas sed magna ut libero consequat elementum. Maecenas euismod pellentesque pulvinar. Morbi sit amet turpis eget dolor rutrum eleifend. Sed bibendum diam nec diam posuere pulvinar. Cras ac bibendum arcu.',
              date: '11/12/2019',
            },
            {
              from: 'user@mdbootstrap.com',
              title: 'How to purchase MDB5 package?',
              message: 'Quisque tempor ligula eu lobortis scelerisque. Mauris tristique mi a erat egestas, quis dictum nibh iaculis. Sed gravida sodales egestas. In tempus mollis libero sit amet lacinia. Duis non augue sed leo imperdiet efficitur faucibus vitae elit. Mauris eu cursus ligula. Praesent posuere efficitur cursus.',
              date: '10/12/2019',
            },
            {
              from: 'user@mdbootstrap.com',
              title: 'Licence renewal',
              message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sed metus ultricies, sollicitudin est nec, blandit turpis. Fusce venenatis nisi volutpat, pharetra elit eu, ullamcorper metus. Vestibulum dapibus laoreet aliquam. Maecenas sed magna ut libero consequat elementum. Maecenas euismod pellentesque pulvinar. Morbi sit amet turpis eget dolor rutrum eleifend. Sed bibendum diam nec diam posuere pulvinar. Cras ac bibendum arcu.',
              date: '09/12/2019',
            },
            {
              from: 'admin@mdbootstrap.com',
              title: 'Black friday offer',
              message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sed metus ultricies, sollicitudin est nec, blandit turpis. Fusce venenatis nisi volutpat, pharetra elit eu, ullamcorper metus. Vestibulum dapibus laoreet aliquam. Maecenas sed magna ut libero consequat elementum. Maecenas euismod pellentesque pulvinar. Morbi sit amet turpis eget dolor rutrum eleifend. Sed bibendum diam nec diam posuere pulvinar. Cras ac bibendum arcu.',
              date: '08/12/2019',
            },
          ];

          const rows = messages.map((email, i) => {
            const getPreview = (message, length) => {
              if (message.length <= length) return message;

              return `${message.slice(0, length)}...`;
            };

            return {
              ...email,
              preview: getPreview(email.message, 20),
              actions: `
                <a role="button" class="star-email-button text-warning" data-mdb-index="${i}">
                  <i class="far fa-star"></i>
                </a>
                <a role="button" class="delete-email-button text-muted ms-2" data-mdb-index="${i}">
                  <i class="fa fa-trash-alt"></i>
                </a>
              `,
            };
          });

          table.addEventListener('rowClick.mdb.datatable', (e) => {
            const { index } = e;
            const { message, title, from } = messages[index];

            modalHeader.innerText = title;
            modalBody.innerHTML = `
              <h6 class="mb-4">From: <strong>${from}</strong></h6>
              <p>${message}</p>
            `;

            modalInstance.show();
          });

          table.addEventListener('render.mdb.datatable', () => {
            setupButtons('star');
            setupButtons('delete');
          })

          const datatableInstance = new mdb.Datatable(table, {
            columns,
            rows,
          });
        
        
    

Datatables - API


Usage

Via data attributes

Using the Datatable component doesn't require any additional JavaScript code - simply add a div wrapper with a datatable class to your table and use data attributes to set all options.

        
            
        <div class="datatable" data-mdb-bordered="true">
        <table>
          <thead>
          <tr>
            <th data-mdb-sort="false" data-mdb-fixed="true" data-mdb-width="100">Column 1</th>
            <th>Column 2</th>
          </tr>
          </thead>
          <tbody>
          <tr>
            <td>Value 1</td>
            <td>Value 2</td>
          </tr>
          </tbody>
        </table>
        </div>
    
        
    

Via JavaScript

If you prefer to render a table with JavaScript, initialize an instance with the mdb.Datatable constructor.

        
            
        const datatableInstance = new mdb.Datatable(
          document.getElementById('my-datatable'),
          {
            columns: [
              { label: 'Column 1', width: 100, fixed: true, sort: false },
              { label: 'Column 2'}
            ],
            rows: [
              ['Value 1', 'Value 2']
            ]
          },
          { 
            bordered: true
          }
        )
    
        
    

Via jQuery

Note: By default, MDB does not include jQuery and you have to add it to the project on your own.

        
            
        $(document).ready(() => {
          $('#my-datatable').datatable(
            {
              columns: [
                { label: 'Column 1', width: 100, fixed: true, sort: false },
                { label: 'Column 2'}
              ],
              rows: [
                ['Value 1', 'Value 2']
              ]
            },
            {
              bordered: true
            }
          )

          // Calling .update() with the jQuery interface:
          $('#my-datatable').datatable('update', { rows: [...], columns: [...]}, { bordered: true, loading: false })
        });
    
        
    

Options

Options can be passed via data attributes or JavaScript. For data attributes, append the option name to data-mdb-, as in data-mdb-all-text="".

Name Type Default Description
allText String 'All' Changes text for All in the pagination select
bordered Boolean false Adds borders to a datatable
borderless Boolean false Removes all borders from a datatable
borderColor String Changes a border color to one of main colors
color String Adds a color class to a datatable (f.e 'bg-dark')
dark Boolean false Changes a font color to white
defaultValue String '-' This string will be used as a placeholder if a row doesn't have a defined value for a column
edit Boolean false Enables edit mode
entries Number 10 Number of visible entries (pagination)
entriesOptions Array [10, 25, 50, 200] Options available to choose from in a pagination select (rows per page). Array with the available options may contain numbers and 'All' to display all entries on single page.
fixedHeader Boolean false When it's set to true, the table's header will remain visible while scrolling
forceSort Boolean false When it's set to true, the table's sort will toggle between two options: ascending and descending. The initial state will not be one of the options.
fullPagination Boolean false Displays additional buttons for the first and last pages
hover Boolean false Changes the background color of a hovered row
loading Boolean false Sets the loading mode - disables interactions and displays a loader
loaderClass String 'bg-primary' The class name for a loader (loading mode)
loadingMessage String 'Loading results...' A message displayed while loading data
maxWidth Number|String Sets a maximum width of a datatable - can be either a string ('10%') or a number of pixels.
maxHeight Number|String Sets a maximum height of a datatable - can be either a string ('10%') or a number of pixels.
selectable Boolean false Enables selecting rows with checkboxes
multi Boolean false Allows selecting multiple rows (selectable mode)
noFoundMessage String 'No matching results found' A message displayed when a table is empty
pagination Boolean true Shows/hides the pagination panel
sm Boolean false Decreases a row's paddings
striped Boolean false Slightly changes the background's color in every other row
rowsText String 'Rows per page': A text indicating a number of rows per page
ofText String 'of' A message displayed as pagination description
clickableRows Boolean false Makes rows clickable
sortField String|Null null Sorts given field on init (works only when data is injected with JS)
sortOrder String 'asc' Defines an order in which initial sorting will sort. Use 'asc' for ascending order, and 'desc' for descending order (works only when data is injected with JS)

Options (column)

Name Type Default Description
label String '' A displayed header of a column
field String '' A field's name - will be used as a key for values in rows
fixed Boolean|String false When set to true, makes a column stick on the left while scrolling. Changing its value to right will do the same on the other side. For this option to work, you need to define width as well.
width Number A column's width in pixels
sort Boolean true Enables/disables sorting for this column
format(cell, value) Function Function runs for each cell, taking the DOM node & cell's value as its parameters

Methods

Name Description Example
update Updates and rerenders datatable. myDatatable.update(data: Object, options: Object)
setActivePage Sets a specific page of entries. Page count starts from 0. myDatatable.setActivePage(index)
search Filters rows so there are only those containing the searched phrase. myDatatable.search(phrase: String, column: String|Array (optional))
dispose Removes the component's instance. myDatatable.dispose()
getInstance Static method which allows you to get the datatable instance associated to a DOM element. Datatable.getInstance(datatableEl)
getOrCreateInstance Static method which returns the datatable instance associated to a DOM element or create a new one in case it wasn't initialized. Datatable.getOrCreateInstance(datatableEl)
        
            
        const datatableInstance = mdb.Datatable.getInstance(document.getElementById('my-datatable'));

        datatableInstance.update({ rows: [...], columns: [...]}, { bordered: true, loading: false })
    
        
    

Events

Name Description
update.mdb.datatable This event fires in an editable mode when a user updates values. You can access the updated data inside a listener's handler with event.rows and event.columns fields.
selectRows.mdb.datatable This event fires when a user select rows with checkboxes. You can acquire more information about selected rows with the following properties of an emitted event: selectedRows: Array, selectedIndexes: Array, allSelected: Boolean
render.mdb.datatable Event emitted after the component renders/updates rows.
rowClick.mdb.datatable Event emitted after clicking on a row
        
            
          const datatable = document.getElementById('my-datatable');

          new mdb.Datatable({ rows: [], columns: []}, { selectable: true, multi: true })

          datatable.addEventListener('selectRows.mdb.datatable', (e) => {
            console.log(e.selectedRows, e.selectedIndexes, e.selectedAll)
          })
      
        
    

Import

MDB UI KIT also works with module bundlers. Use the following code to import this component:

        
            
        import { Datatable } from 'mdb-ui-kit';
    
        
    

CSS variables

        
            
        // .datatable
        --#{$prefix}datatable-color: #{$datatable-color};
        --#{$prefix}datatable-border-color: #{$datatable-border-color};
        --#{$prefix}datatable-striped-color: #{$datatable-striped-color};
        --#{$prefix}datatable-accent-bg: #{$datatable-accent-bg};
        --#{$prefix}datatable-hover-color: #{$datatable-hover-color};
        --#{$prefix}datatable-hover-bg: #{$datatable-hover-bg};
        --#{$prefix}datatable-muted-color: #{$datatable-muted-color};
        --#{$prefix}datatable-active-color: #{$datatable-active-color};
        --#{$prefix}datatable-font-size: #{$datatable-font-size};
        --#{$prefix}datatable-background-color: #{$datatable-background-color};
        --#{$prefix}datatable-table-th-td-max-width: #{$datatable-table-th-td-max-width};
        --#{$prefix}datatable-table-th-td-padding-y: #{$datatable-table-th-td-padding-y};
        --#{$prefix}datatable-table-th-td-padding-x: #{$datatable-table-th-td-padding-x};
        --#{$prefix}datatable-thead-tr-border-width: #{$datatable-thead-tr-border-width};
        --#{$prefix}datatable-thead-th-font-weight: #{$datatable-thead-th-font-weight};
        --#{$prefix}datatable-thead-fixed-cell-background-color: #{$datatable-thead-fixed-cell-background-color};
        --#{$prefix}datatable-tbody-font-weight: #{$datatable-tbody-font-weight};
        --#{$prefix}datatable-tbody-tr-transition: #{$datatable-tbody-tr-transition};
        --#{$prefix}datatable-tbody-tr-last-child-height: #{$datatable-tbody-tr-last-child-height};
        --#{$prefix}datatable-tbody-loader-height: #{$datatable-tbody-loader-height};
        --#{$prefix}datatable-tbody-progress-animation: #{$datatable-tbody-progress-animation};
        --#{$prefix}datatable-tbody-progress-width: #{$datatable-tbody-progress-width};
        --#{$prefix}datatable-tbody-progress-opacity: #{$datatable-tbody-progress-opacity};
        --#{$prefix}datatable-tbody-progress-border-radius: #{$datatable-tbody-progress-border-radius};
        --#{$prefix}datatable-pagination-padding-y: #{$datatable-pagination-padding-y};
        --#{$prefix}datatable-pagination-border-width: #{$datatable-pagination-border-width};
        --#{$prefix}datatable-pagination-nav-font-size: #{$datatable-pagination-nav-font-size};
        --#{$prefix}datatable-pagination-buttons-margin-left: #{$datatable-pagination-buttons-margin-left};
        --#{$prefix}datatable-pagination-button-padding-x: #{$datatable-pagination-button-padding-x};
        --#{$prefix}datatable-sort-icon-transition-duration: #{$datatable-sort-icon-transition-duration};
        --#{$prefix}datatable-sort-icon-left: #{$datatable-sort-icon-left};
        --#{$prefix}datatable-sort-icon-top: #{$datatable-sort-icon-top};
        --#{$prefix}datatable-select-wrapper-font-size: #{$datatable-select-wrapper-font-size};
        --#{$prefix}datatable-select-wrapper-font-weight: #{$datatable-select-wrapper-font-weight};
        --#{$prefix}datatable-sm-th-td-padding-y: #{$datatable-sm-th-td-padding-y};
        --#{$prefix}datatable-sm-th-td-padding-x: #{$datatable-sm-th-td-padding-x};
        --#{$prefix}datatable-sm-tbody-tr-last-child-height: #{$datatable-sm-tbody-tr-last-child-height};
        --#{$prefix}datatable-sm-pagination-padding: #{$datatable-sm-pagination-padding};
        --#{$prefix}datatable-bordered-th-td-border-width: #{$datatable-bordered-th-td-border-width};
        --#{$prefix}datatable-hover-tbody-tr-transition: #{$datatable-hover-tbody-tr-transition};
        --#{$prefix}datatable-dark-select-arrow-input-color: #{$datatable-dark-select-arrow-input-color};
        --#{$prefix}datatable-dark-border-color: #{$datatable-dark-border-color};
        --#{$prefix}datatable-dark-check-border-color: #{$datatable-dark-check-border-color};
        --#{$prefix}datatable-dark-datatable-progress-opacity: #{$datatable-dark-datatable-progress-opacity};
        
        // &.datatable-dark
        --#{$prefix}datatable-color: #{$datatable-dark-color};
        --#{$prefix}datatable-border-color: #{$datatable-dark-border-color};
        --#{$prefix}datatable-active-color: #{$datatable-dark-active-color};
        --#{$prefix}datatable-striped-color: #{$datatable-dark-striped-color};
        --#{$prefix}datatable-accent-bg: #{$datatable-dark-accent-bg};
        --#{$prefix}datatable-hover-bg: #{$datatable-dark-hover-bg};
        --#{$prefix}datatable-hover-color: #{$datatable-dark-hover-color};
      
        // @each $color, $value in $theme-colors
        // &.border-#{$color}
        --#{$prefix}datatable-border-color: #{$value};

        // &.datatable-borderless {
        --#{$prefix}datatable-border-color: #{transparent};
        
        
    

SCSS variables

        
            
        $datatable-color: rgb(33, 37, 41);
        $datatable-border-color: rgb(224, 224, 224);
        $datatable-striped-color: rgb(33, 37, 41);
        $datatable-accent-bg: rgba(0, 0, 0, 0.02);
        $datatable-hover-color: rgb(19, 19, 19);
        $datatable-hover-bg: rgba(0, 0, 0, 0.048);
        $datatable-muted-color: grey;
        $datatable-active-color: rgba(19, 19, 19, 0.05);

        $datatable-font-size: 0.9rem;
        $datatable-background-color: $white;
        $datatable-table-th-td-max-width: 250px;
        $datatable-table-th-td-padding-y: 1rem;
        $datatable-table-th-td-padding-x: 1.4rem;
        $datatable-thead-tr-border-width: 1px;
        $datatable-thead-th-font-weight: 500;
        $datatable-thead-fixed-cell-background-color: $white;

        $datatable-tbody-font-weight: 300;
        $datatable-tbody-tr-transition: all 0.3s ease-in;
        $datatable-tbody-tr-last-child-height: 71px;
        $datatable-tbody-loader-height: 2px;

        $datatable-tbody-progress-animation: datatableProgress 3s ease-in-out;
        $datatable-tbody-progress-width: 45%;
        $datatable-tbody-progress-opacity: 0.5;
        $datatable-tbody-progress-border-radius: 1px;

        $datatable-pagination-padding-y: 0.5rem;
        $datatable-pagination-border-width: 1px;
        $datatable-pagination-nav-font-size: 0.9rem;
        $datatable-pagination-buttons-margin-left: 2rem;
        $datatable-pagination-button-padding-x: 1rem;

        $datatable-sort-icon-transition-duration: 0.3s;
        $datatable-sort-icon-left: 0.4rem;
        $datatable-sort-icon-top: calc(50% - 0.5rem);

        $datatable-select-wrapper-font-size: 0.9rem;
        $datatable-select-wrapper-font-weight: 300;

        $datatable-select-font-size: 1rem;
        $datatable-select-line-height: 1.3;
        $datatable-select-padding-top: 0.4em;
        $datatable-select-padding-right: 1.2em;
        $datatable-select-padding-bottom: 0.3em;
        $datatable-select-padding-left: 0.6em;
        $datatable-select-margin-right: 1.4rem;
        $datatable-select-margin-left: 0.1rem;
        $datatable-select-background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23000'><polygon points='0,0 100,0 50,50'/></svg>");
        $datatable-select-background-position: right 0.4em top 60%, 0 0;
        $datatable-select-background-size: 0.65em auto, 100%;
        $datatable-select-focus-font-weight: 500;
        $datatable-select-option-color: $black;

        $datatable-sm-th-td-padding-y: 0.5rem;
        $datatable-sm-th-td-padding-x: 1.4rem;
        $datatable-sm-tbody-tr-last-child-height: 55px;
        $datatable-sm-pagination-padding: 0.2rem;

        $datatable-dark-check-border-color: $white;
        $datatable-dark-select-background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='white'><polygon points='0,0 100,0 50,50'/></svg>");
        $datatable-dark-datatable-progress-opacity: 0.8;
        $datatable-dark-select-arrow-input-color: white;
        $datatable-dark-border-color: rgb(251, 251, 251);
        $datatable-dark-color: #fff;
        $datatable-dark-border-color: #fff;
        $datatable-dark-striped-color: white;
        $datatable-dark-accent-bg: rgba(255, 255, 255, 0.05);
        $datatable-dark-hover-color: white;
        $datatable-dark-hover-bg: rgba(255, 255, 255, 0.2);
        $datatable-dark-active-color: rgba(255, 255, 255, 0.2);

        $datatable-hover-tbody-tr-transition: background-color 0.2s ease-in;
        $datatable-bordered-th-td-border-width: 1px;
        $datatable-loading-select-background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='grey'><polygon points='0,0 100,0 50,50'/></svg>");