---
title: Build tables with Kibana
description: Instructions and best practices for building tables with {{kib}} Lens in Elastic.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/visualize/charts/tables
products:
  - Elastic Cloud Enterprise
  - Elastic Cloud Hosted
  - Elastic Cloud Serverless
  - Elastic Cloud on Kubernetes
  - Elastic Stack
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Generally available
---

# Build tables with Kibana
Tables are versatile visualizations that display your data in rows and columns, making them ideal for detailed data analysis and comparison. They're perfect for displaying multiple metrics side-by-side, showing individual records, or creating pivot tables that summarize data across different dimensions.
Tables work with any type of data: numeric values, strings, dates, and more. You can organize data using rows, add metrics to analyze, and optionally split metrics into separate columns to create pivot-style views. Tables offer extensive customization options including sorting, filtering, formatting, and coloring.
You can create tables in Kibana using [**Lens**](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/visualize/lens).
![A table visualization in Kibana](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/images/table-charts.png)

## Build a table

Before you start, make sure you have data [indexed into Elasticsearch](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/manage-data/ingest) or [install sample data](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/manage-data/ingest/sample-data). By default, Lens uses [data views](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/find-and-organize/data-views) to access your Elasticsearch data. Data views are created automatically in most cases when you ingest data. You can also [create one manually](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/find-and-organize/data-views) to select just the data that you want. Alternatively, you can use the [ES|QL query mode](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/visualize/esorql) to query your Elasticsearch data directly.
To build a table:
<stepper>
  <step title="Access Lens">
    **Lens** is Kibana's main visualization editor. You can access it:
    - From a dashboard: On the **Dashboards** page, open or create the dashboard where you want to add a table, then add a new visualization.
    - From the **Visualize library** page by creating a new visualization.
  </step>

  <step title="Set the visualization to Table">
    New visualizations default to creating **Bar** charts.Using the dropdown indicating **Bar**, select **Table**.
  </step>

  <step title="Define the data to show">
    1. Select the data view that contains your data.
    2. Define your table structure by dragging fields and defining functions for one or more of these dimensions:
       - **Metrics**: The values to display in columns. You can use aggregation functions like `Sum`, `Average`, and `Count`, or create custom calculations with formulas.
    - **Rows** (optional): Fields that create the rows of your table. Each unique value becomes a row. You can use functions like **Top values**, **Date histogram**, **Intervals**, or **Filters** to organize your rows. You can add multiple fields as rows to create hierarchical groupings and break down the data more granularly.
    - **Split metrics by** (optional): Break metrics into separate columns based on a categorical field, creating a pivot table view.
    3. Optionally, customize individual columns by clicking on any dimension in the layer pane to configure formatting, alignment, coloring, and more.
    The table preview updates to show your metrics as columns. If you added row dimensions, each unique value creates a separate row. If you added a **Split metrics by** dimension, metrics are broken into multiple columns by category.Refer to [Build tables with Kibana > Table settings](#settings) to find all configuration options for your table.
  </step>

  <step title="Customize the table to follow best practices">
    Tweak the appearance of the table to your needs. Consider the following best practices:
    <definitions>
      <definition term="Make it scannable">
        Use consistent formatting and alignment. For example, you can right-align numbers for easier comparison, and left-align text for readability.
      </definition>
      <definition term="Use color purposefully">
        Apply color to values or cells to highlight important data or patterns. Avoid using too many colors that might distract from the data.
      </definition>
      <definition term="Add context with summary rows">
        Use summary rows to show totals, averages, or other aggregate values that help users understand the overall picture.
      </definition>
      <definition term="Enable interactivity">
        Turn on **Directly filter on click** to let users click on values to filter the dashboard or drill down into data.
      </definition>
      <definition term="Control density">
        Adjust table density based on your use case. Use **Compact** for fitting more rows, **Expanded** for better readability.
      </definition>
    </definitions>
    Refer to [Build tables with Kibana > Table settings](#settings) for a complete list of options.
  </step>

  <step title="Save the table">
    - If you accessed Lens from a dashboard, select **Save and return** to save the visualization and add it to that dashboard, or select **Save to library** to add the visualization to the Visualize library and be able to add it to other dashboards later.
    - If you accessed Lens from the Visualize library, select **Save**. A menu opens and lets you add the visualization to a dashboard and to the Visualize library.
  </step>
</stepper>


## Advanced table scenarios


### Create pivot tables

Tables can display data in a pivot-style format by using the **Split metrics by** dimension. This creates separate columns for each unique value of the split field, which is great for comparing metrics across different categories.
To create a pivot table:
1. Create a **Table** visualization.
2. Add a dimension to **Rows**.
3. Add one or more metrics.
4. Drag a categorical field to **Split metrics by** to create separate columns for each unique value.

For example, you could show visits per date in rows, split by the top 3 hours of the day with most traffic, and add various metrics such as the number of visits or the percentage of successful requests. This creates a pivot table showing the various metrics for each hour of the day with the most traffic.
![Example of a table in Lens using the Split metrics by functionality](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/images/lens-table-breakdown-by-example.png)
Refer to [Analyze the data in a table](/elastic/docs-content/pull/5400/explore-analyze/dashboards/create-dashboard-of-panels-with-ecommerce-data#view-customers-over-time-by-continents) for a detailed example.

### Use formulas in tables

Tables support Lens formulas, which let you create calculated columns with custom logic. You can use formulas to:
- Calculate percentages or ratios between metrics
- Compare current values to time-shifted values
- Apply mathematical operations across multiple fields
- Create conditional calculations

To add a formula to a table:
1. In the **Metrics** dimension, select **Add a field**.
2. Select **Formula** from the function list.
3. Enter your formula using the available functions and fields.
4. Customize the column name and formatting.

Refer to [Lens > Use formulas to perform math](/elastic/docs-content/pull/5400/explore-analyze/visualize/lens#lens-formulas) for formula examples, including time-shifting comparisons and mathematical operations, and the `documentation` **Formula reference** available from Lens.

### Use emojis in tables

ES|QL query results can include emoji characters, which means you can use them in your ES|QL visualizations. Combined with `EVAL` and `CASE` functions, this opens up options like mapping values to colored status indicators (🟢, 🟠, 🔴), adding visual labels, or highlighting specific categories.
This example uses the Kibana sample web logs data to build a status table that shows the success rate per host, with a colored status indicator.
![Table visualization showing success rate per host with emoji status indicators](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/images/esql-table-emoji.png)

<tip>
  Because they're part of the query results, you can use them in any visualization type that displays text fields, such as bar charts with emoji labels or metric panels with status indicators.
</tip>

Before you begin, ensure you have the sample web logs data installed. In Kibana, go to **Integrations** and search for **Sample data**. On the **Sample data** page, expand the **Other sample data sets** section and add **Sample web logs**.
To create the visualization:
1. Open a dashboard and add a new ES|QL visualization:
   - <applies-to>Elastic Cloud Serverless: Generally available</applies-to> <applies-to>Elastic Stack: Generally available since 9.2</applies-to> Select **Add** > **New panel** in the toolbar, then choose **ES|QL** under **Visualizations**.
- <applies-to>Elastic Stack: Generally available from 9.0 to 9.1</applies-to> Click **Add panel** in the dashboard toolbar, then choose **ES|QL**.
2. Enter the following query:
   ```esql
   FROM kibana_sample_data_logs
   | EVAL is_success = CASE(response >= "200" AND response < "300", 1, 0) 
   | STATS 
       total_requests = COUNT(*),
       successful_requests = SUM(is_success)
     BY host.keyword 
   | EVAL success_rate = ROUND(successful_requests * 100.0 / total_requests, 1) 
   | EVAL status = CASE( 
       success_rate >= 92, "🟢",
       success_rate >= 90, "🟠",
       "🔴"
     )
   | KEEP host.keyword, status, success_rate, successful_requests, total_requests 
   | SORT success_rate DESC
   ```
   1. Create a binary flag: 1 for successful responses (2xx), 0 otherwise.
2. Group by host and use `SUM` to count successes.
3. Calculate the success rate as a percentage.
4. Map the success rate to emoji indicators based on thresholds.
5. Select and order the columns for the table output.
3. Run the query. A visualization appears with one row per host and an emoji status column. If Kibana suggests a different visualization type, select **Table** from the visualization type dropdown.
4. Optionally, configure the table appearance in the visualization settings:
   - To reorder columns, rearrange the metrics in the **Metrics** section.
- To rename a column, select the metric and update its **Name** in the appearance options.
5. Select **Apply and close** to save the visualization to your dashboard.
6. Optionally, once the panel is saved, select the panel title to give it a meaningful name like `Status per host`.

Once you have your visualization working, you can add [controls](/elastic/docs-content/pull/5400/explore-analyze/dashboards/add-controls#add-variable-control) to filter by host or time range, use [LOOKUP JOIN](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/query-languages/esql/esql-lookup-join) to enrich your data with metadata from other indices, or create [alerts](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/alerting/alerts/rule-type-es-query) based on the same query to get notified when status changes.

## Table settings

Customize your table to display exactly the information you need, formatted the way you want.

### Metrics settings

<definitions>
  <definition term="Value">
    The metrics to display in your table columns. When you drag a field onto the table, Kibana suggests a function based on the field type. You can change it and use aggregation functions like `Sum`, `Average`, `Count`, `Median`, and more, or create custom calculations with formulas.
    Each metric becomes its own column in the table. If you use [**Split metrics by**](#columns-options), each metric is further split into multiple columns.
    Refer to [Lens > Use formulas to perform math](/elastic/docs-content/pull/5400/explore-analyze/visualize/lens#lens-formulas) for examples, or to the `documentation` **Formula reference** available from Lens.
    <dropdown title="Advanced settings">
      Depending on the data you defined, several options allow you to apply additional filtering to the data taken into account to compute the final value to show.Based on the type of visualization you're creating, only some of the following options can be available:
      - **Normalize by unit**: Normalize the metric values to show per unit of time.
      - **Filter by**: Specify a query.
      - **Reduced time range**: Reduce the time range specified on the dashboard's time filter by the specified duration.
      - **Time shift**: Shift the time range by the specified duration. This is useful if the value should use a different time range than the one selected on the dashboard.
      - **Hide zero values**: Don't show values equal to zero. This option is on by default.
    </dropdown>
  </definition>
  <definition term="Appearance">
    Define the formatting and behavior of each metric column, including:
    - **Name**: The column header label. By default, the chart uses the function or formula name. It's a best practice to customize this with a meaningful title.
    - **Value format**: Choose to display the value as number, percent, bytes, bits, duration, or with a custom format that you can define.
    - **Text alignment**: Align the values in the column to the **Left**, **Center**, or **Right**.
    - **Color by value**: Apply colors to cell backgrounds or text based on values. Choose between:
      - **None**: No coloring (default).
    - **Cell**: Apply colors to the cell's background based on its value.
    - **Text**: Apply colors to the cell's text based on its value. Define color ranges and rules to highlight important data patterns
    - **Color mapping**: Define the colors to apply to each cell of the column based on its value. Refer to [Lens > Assign colors to terms](/elastic/docs-content/pull/5400/explore-analyze/visualize/lens#assign-colors-to-terms) for more details.
    - **Hide column**: Hide this column from the table display while keeping it available for sorting or other operations.
    - **Summary row**: Add a row at the bottom of the table showing an aggregate value for this column. You can choose the aggregation function (`Sum`, `Average`, `Min`, `Max`, `Count`) and customize the **Summary label**.
  </definition>
</definitions>


### Rows settings

<definitions>
  <definition term="Data">
    Define which fields create the rows of your table. Drag a field to the **Rows** dimension, and Kibana suggests an appropriate function based on the field type.
    - **Functions**:
      - **Top values**: Show the most common values of a categorical field. Configure the number of values to display, ranking criteria, and sort direction.
      - **Field**: Select the field to group by. You can add up to 4 fields. When multiple fields are selected, each row represents a unique combination of values across those fields. You can reorder the fields by dragging them to change their priority.
    - **Number of values**: How many top values to display
      - **Rank by**: Specifies the dimension the top values are ranked by. Available options:
      - **Count of records**: Rank by the number of documents containing each value. This is the default when a metric is defined.
      - **Alphabetical**: Rank by the term key alphabetically. This is the default when no metric is defined.
      - **Rarity**: Find terms that appear in very few documents, using a [rare terms aggregation](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/aggregations/search-aggregations-bucket-rare-terms-aggregation). You can configure the **Max doc count** to set the maximum number of documents a term can appear in to be considered rare (default: 1, max: 100). Only available for non-numeric fields and single-field terms.
      - **Significance**: Find statistically unusual terms compared to the overall data set, using a [significant terms aggregation](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/aggregations/search-aggregations-bucket-significantterms-aggregation). Only available for `keyword` fields and single-field terms.
      - **Custom**: Define a custom metric aggregation to rank by (for example, rank by the sum of a numeric field rather than by count).
      - **Rank direction**: Ascending or descending order. Disabled when **Rank by** is set to **Rarity** or **Significance**.
      <dropdown title="Advanced settings">
      Several advanced options allow you to refine the behavior of the breakdown:
      - **Include documents without the selected field**: Off by default.
      - **Group remaining values as "Other"**: On by default.
      - **Enable accuracy mode**: This option improves results for high-cardinality data, but increases the load on the Elasticsearch cluster.
      - **Include values**: Values from the breakdown dimension to always show a tile for.
      - **Exclude values**: Values from the breakdown dimension to always exclude from the displayed tiles.
      </dropdown>
    - **Date histogram**: Group data by time intervals. Configure the time interval and how to handle date formatting.
      - **Field**: Select the date field to use for the time-based grouping.
      - **Include empty rows**: This option is on by default. Turn it off to exclude empty rows from the data.
      - **Bind to global time picker**: Associate the selected field to the Lens or dashboard main time selector.
      - **Minimum interval**: Define the time interval for aggregating the data. For example, `30s`, `20m`, `24h`, `2d`, `1w`, `1M`
      - **Drop partial intervals**: Exclude incomplete intervals from the data. This option is off by default.
    - **Intervals**: Create numeric ranges for continuous data. Useful for grouping numeric fields into buckets. You can define the interval granularity or specify custom ranges.
      - **Field**: Select the numeric field to create intervals from.
      <dropdown title="How does interval granularity work?">
      Interval granularity divides the field into evenly spaced intervals based on the minimum and maximum values for the field.The size of the interval is a "nice" value. When the granularity of the slider changes, the interval stays the same when the “nice” interval is the same. The minimum granularity is 1, and the maximum value is histogram:maxBars. To change the maximum granularity, go to Advanced settings.Intervals are incremented by 10, 5 or 2. For example, an interval can be `100` or `0.2`.
      </dropdown>
    - **Filters**: Define custom KQL filters to create specific row groups. Each filter creates one row in the table.
    - **Collapse by**: Aggregate rows that share the same value for this field into a single row, combining their metrics (for example, sum or average for each group). This is useful when you want to display a consolidated result for grouped values instead of individual rows.
  </definition>
  <definition term="Appearance">
    - **Name**: Customize the column header label for the row dimension.
    - **Value format**: Choose to display the value as number, percent, bytes, bits, duration, or with a custom format that you can define.
    - **Text alignment**: Align the values in the column to the **Left**, **Center**, or **Right**.
    - **Color by value**: Apply colors to cell backgrounds or text based on values. Choose between:
      - **None**: No coloring (default).
    - **Cell**: Apply colors to the cell's background based on its value.
    - **Text**: Apply colors to the cell's text based on its value. Define color ranges and rules to highlight important data patterns
    - **Color mapping**: Define the colors to apply to each cell of the column based on its value. Refer to [Lens > Assign colors to terms](/elastic/docs-content/pull/5400/explore-analyze/visualize/lens#assign-colors-to-terms) for more details.
    - **Hide column**: Hide this column from the table display while keeping it available for sorting or other operations.
    - **Directly filter on click**: Make the values in this column clickable, so clicking a value adds a filter to your visualization or dashboard for that value. This interactivity is helpful for quickly drilling down into data.
  </definition>
</definitions>


### Split metrics by settings

<definitions>
  <definition term="Data">
    Optionally split your metrics into separate columns based on a categorical field. This creates a pivot table view where each unique value of the split field becomes its own column. This is useful for comparing the same metric across different categories side by side.
    - **Functions**:
      - **Top values**: Show the most common values of a categorical field. Configure the number of values to display, ranking criteria, and sort direction.
      - **Field**: Select the field to group by. You can add up to 4 fields. When multiple fields are selected, each column group represents a unique combination of values across those fields. You can reorder the fields by dragging them to change their priority.
    - **Number of values**: How many top values to display
      - **Rank by**: Specifies the dimension the top values are ranked by. Available options:
      - **Count of records**: Rank by the number of documents containing each value. This is the default when a metric is defined.
      - **Alphabetical**: Rank by the term key alphabetically. This is the default when no metric is defined.
      - **Rarity**: Find terms that appear in very few documents, using a [rare terms aggregation](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/aggregations/search-aggregations-bucket-rare-terms-aggregation). You can configure the **Max doc count** to set the maximum number of documents a term can appear in to be considered rare (default: 1, max: 100). Only available for non-numeric fields and single-field terms.
      - **Significance**: Find statistically unusual terms compared to the overall data set, using a [significant terms aggregation](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/aggregations/search-aggregations-bucket-significantterms-aggregation). Only available for `keyword` fields and single-field terms.
      - **Custom**: Define a custom metric aggregation to rank by (for example, rank by the sum of a numeric field rather than by count).
      - **Rank direction**: Ascending or descending order. Disabled when **Rank by** is set to **Rarity** or **Significance**.
      <dropdown title="Advanced settings">
      Several advanced options allow you to refine the behavior of the breakdown:
      - **Include documents without the selected field**: Off by default.
      - **Group remaining values as "Other"**: On by default.
      - **Enable accuracy mode**: This option improves results for high-cardinality data, but increases the load on the Elasticsearch cluster.
      - **Include values**: Values from the breakdown dimension to always show a tile for.
      - **Exclude values**: Values from the breakdown dimension to always exclude from the displayed tiles.
      </dropdown>
    - **Date histogram**: Group data by time intervals. Configure the time interval and how to handle date formatting.
      - **Field**: Select the date field to use for the time-based grouping.
      - **Include empty rows**: This option is on by default. Turn it off to exclude empty rows from the data.
      - **Bind to global time picker**: Associate the selected field to the Lens or dashboard main time selector.
      - **Minimum interval**: Define the time interval for aggregating the data. For example, `30s`, `20m`, `24h`, `2d`, `1w`, `1M`
      - **Drop partial intervals**: Exclude incomplete intervals from the data. This option is off by default.
    - **Intervals**: Create numeric ranges for continuous data. Useful for grouping numeric fields into buckets. You can define the interval granularity or specify custom ranges.
      - **Field**: Select the numeric field to create intervals from.
      <dropdown title="How does interval granularity work?">
      Interval granularity divides the field into evenly spaced intervals based on the minimum and maximum values for the field.The size of the interval is a "nice" value. When the granularity of the slider changes, the interval stays the same when the “nice” interval is the same. The minimum granularity is 1, and the maximum value is histogram:maxBars. To change the maximum granularity, go to Advanced settings.Intervals are incremented by 10, 5 or 2. For example, an interval can be `100` or `0.2`.
      </dropdown>
    - **Filters**: Define custom KQL filters to create specific column groups. Each filter creates one column in the table.
  </definition>
  <definition term="Appearance">
    - **Name**: Customize the split dimension. This name is not used on the table.
  </definition>
</definitions>


### General table settings

When creating or editing a table visualization, you can customize several appearance options. To do that, look for the `brush` icon.
<definitions>
  <definition term="Density Elastic Stack: Generally available since 9.1 Elastic Cloud Serverless: Generally available">
    Control how much space each row occupies. Choose between:
    - **Compact**: Minimal spacing, fits more rows in less space
    - **Normal**: Balanced spacing (default)
    - **Expanded**: More generous spacing for improved readability
  </definition>
  <definition term="Max header cell lines">
    Set the maximum number of lines that column headers can span. When header text is longer than this setting, it is truncated with an ellipsis. Use `Auto` to let Kibana determine the appropriate height, or set a specific number like `1`, `2`, or `3`.
  </definition>
  <definition term="Body cell lines">
    Set the number of lines that body cells display. When cell content exceeds this limit, it is truncated with an ellipsis. Use `Auto` to automatically adjust based on content, or set a specific number like `1`, `2`, or `3` for consistent row heights. Setting this to `1` creates more compact tables, while higher values allow more content to be visible.
  </definition>
  <definition term="Paginate table">
    Toggle pagination on or off. When enabled:
    - The table displays a limited number of rows per page.
    - Navigation controls appear at the bottom of the table when the table contains at least 10 items. By default, 10 rows appear per page. Users of the dashboard will be able to select a different number.
    - This is helpful for tables with many rows to improve performance and readability.
    When disabled, all rows appear in a scrollable view (up to the maximum returned by the query).
  </definition>
</definitions>


## Table examples

The following examples show various configuration options you can use for building effective tables.
<definitions>
  <definition term="Top pages by unique visitors">
    Display the most visited pages on your website with the number of unique visitors:
    - **Rows**: `request.keyword` field using **Top values** function
      - **Number of values**: `5`
    - **Metrics**: `clientip` field using **Unique count** function
      - **Value format**: `Number`
    - **Text alignment**: `Right`
    ![Table showing top pages by unique visitors](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/images/kibana-table-with-request-keyword-and-client-ip-8.16.0.png)
  </definition>
  <definition term="Sales by date and continent (pivot table)">
    Create a pivot table showing customer counts across different continents over time:
    - **Rows**: `order_date` field using **Date histogram** function
      - **Minimum interval**: `1d`
    - **Name**: `Sales per day`
    - **Metrics**: `customer_id` field using **Unique count** function
    - **Split metrics by**: `geoip.continent_name` field using **Top values** set to `3`
    ![Table showing customers over time by continent](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5400/explore-analyze/images/kibana-lens_table_over_time.png)
  </definition>
  <definition term="Document comparison with custom ranges">
    Compare metrics across custom-defined ranges:
    - **Rows**: `bytes` field using **Intervals** function
      - **Ranges**:
      - `0` → `10240`, labeled `Below 10KB`
    - `10240` → `+∞`, labeled `Above 10KB`
    - **Name**: `File size`
    - **Metrics**: `bytes` field using **Sum** function
      - **Name**: `Total bytes transferred`
    - **Value format**: `Bytes`
    - **Text alignment**: `Right`
    - **Additional styling**:
      - **Color by value**: Dynamic coloring to highlight ranges with higher byte transfers
  </definition>
  <definition term="Weekly sales with percentage change">
    Show week-over-week sales trends with calculated percentage changes:
    - **Rows**: `order_date` field using **Date histogram** function
      - **Minimum interval**: `1w`
    - **Name**: `Week`
    - **Metrics** (two columns):
      1. `Records` using **Count** function
       - **Name**: `Orders this week`
    2. **Formula**: `count() / count(shift='1w') - 1`
       - **Name**: `Change from last week`
    - **Value format**: `Percent`, 2 decimals
    - **Color by value**: Dynamic (green for positive growth, red for negative)
    - **Text alignment**: `Right`
  </definition>
</definitions>