table
Displays a data set as tabular data.
Properties¶
Name | Type | Default | Description |
---|---|---|---|
(★) |
Any dynamic |
Required | The data to be represented in this table. This property can be indexed to define other data for comparison. |
page_size |
int |
100 | For a paginated table, the number of visible rows. |
allow_all_rows |
bool |
False | For a paginated table, adds an option to show all the rows. |
show_all |
bool |
False | For a paginated table, show all the rows. |
auto_loading |
bool |
False | If True, the data will be loaded on demand. |
width[column_name] |
str |
The width of the indicated column, in CSS units (% values are not supported). |
|
selected |
Union[list[int],str] dynamic |
The list of the indices of the rows to be displayed as selected. |
|
page_size_options |
Union[list[int],str] |
[50, 100, 500] | The list of available page sizes that users can choose from. |
columns |
Union[str,list[str],dict[str,dict[str,Union[str,int]]]] |
All columns | The list of the column names to display.
If columns is omitted or set to None, all columns of data are represented. |
date_format |
str |
"MM/dd/yyyy" | The date format used for all date columns when the format is not specifically defined. |
number_format |
str |
The number format used for all number columns when the format is not specifically defined. |
|
group_by[column_name] |
bool |
False | Indicates, if True, that the given column can be aggregated. |
apply[column_name] |
str |
"first" | The name of the aggregation function to use. |
row_class_name |
Union[str, Callable] |
Allows for styling rows.
See below for more details. |
|
cell_class_name[column_name] |
Union[str, Callable] |
Allows for styling cells.
See below for more details. |
|
tooltip |
Union[str, Callable] |
Enables tooltips on cells. |
|
tooltip[column_name] |
Union[str, Callable] |
Enables tooltips on cells at a column level. |
|
format_fn[column_name] |
Union[str, Callable] |
Defines custom formatting for table cells. This property must be a function or the name of a function that returns a formatted string for each cell.
For more details, see the section. |
|
width |
str |
"100%" | The width of this table control, in CSS units. |
height |
str |
"80vh" | The height of this table control, in CSS units. |
filter |
bool |
False | Indicates, if True, that all columns can be filtered. |
filter[column_name] |
bool |
False | Indicates, if True, that the indicated column can be filtered. |
nan_value |
str |
"" | The replacement text for NaN (not-a-number) values. |
nan_value[column_name] |
str |
"" | The replacement text for NaN (not-a-number) values for the indicated column. |
editable |
bool dynamic |
False | Indicates, if True, that all cells can be edited. |
editable[column_name] |
bool |
editable | Indicates, if False, that the indicated column cannot be edited, even if editable is True. |
on_edit |
Union[bool, Callable] |
default implementation | A function or the name of a function triggered when an edited cell is validated.
If this property is not set, the table will use the default implementation for editing cells. |
on_add |
Union[bool, Callable] |
A function or the name of a function that is triggered when the user requests a row to be added to the table.
If this property is not set, the table uses the default implementation for adding a new row If this property is set to False, you cannot add new rows. |
|
on_delete |
Union[bool, Callable] |
default implementation | A function or the name of a function triggered when a row is deleted.
If this property is not set, the table uses the default implementation for deleting rows. |
on_action |
Union[str, Callable] |
A function or the name of a function that is triggered when the user selects a row.
|
|
size |
str |
"small" | The size of the rows. |
rebuild |
bool dynamic |
False | If set to True, this allows to dynamically refresh the columns. |
lov[column_name] |
Union[list[str],str] |
The list of values of the indicated column. |
|
downloadable |
bool |
False | If True, a clickable icon is shown so the user can download the data as CSV. |
on_compare |
Union[str, Callable] |
A function or the name of a function that compares data. This function should return a structure that identifies the differences between the different data passed as name. The default implementation compares the default data with the data[1] value. |
|
use_checkbox |
bool |
False | If True, boolean values are rendered as a simple HTML checkbox. |
sortable |
bool |
True | If False, the table provides no sorting capability. Individual columns can override this global setting, allowing specific columns to be marked as sortable or non-sortable regardless of value of sortable, by setting the sortable property to True or False accordingly, in the dictionary for that column in the columns property value. |
active |
bool dynamic |
True | Indicates if this component is active. |
id |
str |
The identifier that is assigned to the rendered HTML component. |
|
properties |
dict[str, Any] |
Bound to a dictionary that contains additional properties for this element. |
|
class_name |
str dynamic |
The list of CSS class names that are associated with the generated HTML Element. |
|
hover_text |
str dynamic |
The information that is displayed when the user hovers over this element. |
(★)data
is the default property for this visual element.
Details¶
Data types¶
All the data sets represented in the table control must be assigned to its data property.
The supported types for the data property are:
- A list of values:
When receiving a data that is just a series of values, the table is made of a single column holding the values at the corresponding index. The column name is then "0". - A Pandas DataFrame:
Taipy tables then use the same column names as the DataFrame's. - A dictionary:
The value is converted into a Pandas DataFrame where each key/value pair is converted to a column named key and the associated value. Note that this method only works when all the dictionary's values are series of identical lengths. - A list of lists of values:
All the lists must be the same length. The table control creates one row for each list in the collection. - A NumPy series:
Taipy internally builds a Pandas DataFrame with the provided data.
Polars data types support
Taipy Enterprise edition provides native support for the
polars.LazyFrame
,
polars.DataFrame
or polars.Series
data types in the data property, including for edits.
Display modes¶
The table component supports three display modes:
- paginated: you can choose the page size and page size options. The allow_all_rows property allows adding an option to show a page with all rows.
- unpaginated: all rows and no page are shown. That is the setting when the show_all property is set to True.
- auto_loading: the pages are loaded on demand depending on the visible area. That is the behavior when the auto_loading property is set to True.
Customizing columns¶
The table
control has properties that let you customize which columns are displayed and in what
order and apply a number format to the numerical and date values.
You can use the columns property to customize what columns are displayed and how they should be displayed.
To demonstrate the capabilities of columns, we will use the following dataset:
data = {
"date" : [datetime.datetime(year=2000, month=3, day=d) for d in [12, 15, 18, 19, 31]],
"volume" : [6533, 1578, 1881, 2373, 8646],
"price" : [152.611, 168.582, 193.798, 101.025, 159.071],
}
Default settings¶
Let's create a table control without specifying the columns property:
Definition
<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")
With that definition, the representation of the control is similar to the following image:


Selecting columns¶
Let's say you don't want to display the "volume" column. To indicate that, you can specify the list of column names to display in the columns property:
Definition
<|{data}|table|columns=date;price|>
<taipy:table columns="date;price">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="date;price")
Here is how the table appears with that definition:


Formatting numbers globally¶
The "price" column displays values with too many decimal places, which is unusual for displaying
a price.
The number_format property can be set to a format string that applies to
all numerical values in the table. The syntax for this string is specified by the Python string
format() function.
The following control definition indicates that all numbers should be represented with two decimal places:
Definition
<|{data}|table|number_format=%.02f|>
<taipy:table number_format="%.02f">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", number_format="%.02f")
And the result looks like this:


Although we only have two decimal places for stock prices, which was the point of setting the number_format property, the format has impacted all numerical values, including the values of the "volume" column, which is not what we want: volumes are integer values, they should have no decimal places at all.
Formatting numbers in specific columns¶
You can provide a specific format for a particular column by setting the columns property to a dictionary that indicates what format should be used for what columns.
The keys of this dictionary must be a column name, and the value for a given key must be another
dictionary with specific key/value pairs.
For our use case, we just want to use a format for the "value" column.
Here is how we can define the dictionary that can be used by the table control to apply the format where it is required:
columns = {
"date" : {},
"volume" : {},
"price" : {"format": "$%.02f"},
}
We can set that dictionary to the columns property:
Definition
<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")
Here is how the table now looks:


You can see that the format only impacts the values of the "price" column.
Formatting dates¶
You can use the date_format property to format dates in the table.
This property uses the syntax of the date-fns.format()
function to convert a datetime
object to a string.
With the same dataset, here is a table control definition that uses the date_format property:
Definition
<|{data}|table|date_format=MMM do|>
<taipy:table date_format="MMM do">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", date_format="MMM do")
The value "MMM do" indicates that the conversion of the date to a string will begin with the abbreviation of the month name, followed by the day number in the month with the index suffix.
See how the dates of the "date" column are now represented:


Note that if you need to apply a date format to a specific column, you need to create a columns dictionary as demonstrated above and set the format property for the columns that contain the date values.
Custom formatting¶
You can specify a Python function as the value of the format_fn[column_name] property. This function is invoked for every cell of the column when the table is rendered. This function allows complete control over the formatting, enabling any type of string representation.
The formatting function receives the following parameters:
- state (
State
): The current state instance, which can be used to access additional variables required for formatting. - value (Any): The cell value to be formatted.
- index (int): The index of the row containing the cell.
- row (Any): All the values for the row. You can use values from other columns in the same row to influence the formatting.
- column_name (str): The name of the column being formatted.
These arguments allow you to create flexible and dynamic formatting logic based on the row, value, and state.
For a complete example, refer to this section.
Setting the column titles¶
By default, a column title reflects the name of the collection that it represents. You can change that by setting the title key of a column-specific dictionary.
Let's specify a column definition that customizes the column titles:
columns = {
"date" : {"title": "Date"},
"volume" : {"title": "Volume"},
"price" : {"title": "Price ($)", "format": "%.02f"},
}
Using the control definition that we have used in the example above, here is how the table looks like:


Ordering columns¶
When looking at stock exchange data tables, the most common representation is to display the stock
price before the exchanged volume. The default behavior of the table
control is to show the
columns in the order they appear in the object set to the data property.
To reorder the columns, you can define the control with the columns property set to a list of column names in the order you want them displayed:
Definition
<|{data}|table|columns=date;price;volume|>
<taipy:table columns="date;price;volume">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="date;price;volume")
This definition produces the following table representation:


An alternative way to order columns that could be more suitable for your situation is to provide an index value (starting at 0) to the index property of the dictionary set for the appropriate column in a dictionary set to the columns property.
You can define a dictionary as follows:
columns = {
"date" : {"index": 0},
"volume" : {"index": 2},
"price" : {"index": 1},
}
The definition of the control would use that dictionary:
Definition
<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")
And the resulting display would be the same.
Editing the table content¶
The table control provides interface elements that help modify the bound data.
These elements let users:
- edit cell content;
- add new rows;
- delete existing rows.
The presence of these UI elements depends on the control configuration:
- icon (Add):
- Appears in the top-left corner of the table when editable is set to True and on_add is not set to False.
- Clicking this icon can add new rows to the table, as described in this section.
- icon (Delete):
- Appears to the left of each row when editable is set to True and on_delete is not set to False.
- Clicking this icon can remove rows, as explained in the this section.
- icon (Edit):
- Appears to the right of each cell if editable is set to True.
- It also appears to the right of each cell in a specific column ("column_name") if editable[column_name] is set to True, even when editable is set to False.
- It does not appear to the right of cells in a given column ("column_name") if editable is set to False, even when editable[column_name] is set to True.
Here are the different actions provided by the table control and explanations about how you can leverage them to fit the needs of your application.
Adding a row¶
When a table control is editable (i.e., either the editable property is set to True or the editable[] property is True for any of the table's columns), and the on_add property is not set to False, an icon (Add) appears in the top-left corner of the table:


The user can click this icon to add a new row to the table. This action triggers the callback function defined by the on_add property. The callback function should handle the creation and insertion of the new row into the table.
Default implementation
If the on_add property is not explicitly defined, the default behavior will invoke
the method Gui.table_on_add()
. This method inserts a new row at the top of the dataset. By
default, all values in the new row are initialized to 0 (adapted to the data type of the column) or
an empty string for columns of type str
.
Custom implementation
The implementation of the callback function depends heavily on the data structure used in the data property and the specific side effects required by your application.
The callback function receives the following parameters:
- state: the client's state;
- var_name: the name of the variable bound to the control's data property;
- payload: a dictionary containing additional information, including:
- "index": the index in the table where the new row should be inserted.
Here is an example of a possible callback function implementation.
In this scenario, the
control's data property holds a Pandas DataFrame, and the on_add
property is set to the insert_row() function:
def insert_row(state: State, var_name: str, payload: dict):
df = state.data
# Retrieve the insertion index from the payload
index = payload["index"]
# Define the new row data (Ensure the types match the DataFrame's columns)
new_row = ["Julius Caesar", -100]
if index > 0:
# Create a new DataFrame for the new row
new_df = pd.DataFrame(new_row, columns=["Name", "BirthYear"])
# Split the existing DataFrame and insert the new row
rows_before = df.loc[:index-1]
rows_after = df.loc[index+1:]
state.data = pd.concat([rows_before, new_df, rows_after], ignore_index=True)
else:
# Insert the new row at the beginning
state.data.loc[-1] = new_row # Insert new row
state.data.index = state.data.index + 1 # Adjust the index
state.data = state.data.sort_index() # Sort the index to maintain order
Note that in this implementation, after the row is added, the page refreshes automatically because state.data is updated.
You can simplify the code significantly by calling Gui.table_on_add()
, which accepts a series of
values to initialize the new row. This avoids the need to manually handle DataFrame splitting or
indexing.
An example below demonstrates this functionality in this section.
Deleting a row¶
When a table control is editable (i.e., either the editable property is set to True or the editable[] property is True for any of the table's columns), and the on_delete property is not set to False, a icon (Delete) appears to the left of each row:


The user can click this icon to initiate the row deletion process.
Once clicked, the icon changes to the following:


The user must then confirm the deletion by clicking the icon (Apply) or cancel the operation by clicking the icon (Cancel).
If the deletion is confirmed, Taipy GUI triggers the callback function specified in the on_delete property.
Default implementation
If the on_delete property is not explicitly defined, the default behavior is to
call the Gui.table_on_delete()
method. This method automatically removes the selected row from
the dataset.
Custom implementation
For a custom implementation of the on_delete callback function, the logic will depend on the structure of the data stored in the data property and any specific side effects your application needs to manage.
The callback function receives the following parameters:
- state: the client's state;
- var_name: the name of the variable bound to the control's data property;
- payload: a dictionary containing additional information, including:
- "index": the index of the row to be removed.
Below is an example implementation of a callback function, assuming the control’s data property is bound to a Pandas DataFrame, and the on_delete property is set to the function remove_row():
def remove_row(state: State, var_name: str, payload):
state.data = state.data.drop(payload["index"])
If you prefer not to handle the Pandas API or are unsure of the data structure in the
data property, you can delegate the task to the Gui.table_on_delete()
method, which
manages the deletion process for you.
In the example below, there is a demonstration of this functionality in this section.
Editing cells¶
If the on_edit property is not set to False and the table columns are editable
(i.e., either editable[column_name] is set to True regardless of the
editable property value, or editable[column_name] is
not set to False and editable is set to True), the icon (Edit)
appears at the right end of each editable cell. Users can click this icon to modify the values of
individual
cells.
Here is how this icon looks:


When the user clicks this icon, the cell becomes editable:


The user can enter a new value for the cell and either click the icon (Apply) or press
the Enter key to confirm the change. This action triggers the callback function defined by the
on_edit property (or the built-in Gui.table_on_edit()
method if
on_edit is not set) to process the updated value.
Alternatively, the user can cancel the operation by clicking the icon (Cancel) or
pressing the ESC key.
Enumerated values
A table control can be configured so that cell values are constrained to predefined options. In such cases, the editing interaction may differ slightly, as explained in the section on enumerated values.
Default implementation
If the on_edit property is not explicitly defined, the default behavior is to call
the Gui.table_on_edit()
method. This method updates the value of the specified cell with the
user-provided input.
Custom implementation
The implementation of the on_edit callback function will vary depending on the data structure used in the data property, the data types involved, and the application’s requirements for data validation or transformation.
The callback function receives the following parameters:
- state: the client's state;
- var_name: the name of the variable bound to the control's data property;
- payload: a dictionary containing additional information, including:
- "index": the row index of the cell to be modified.
- "col": the column name of the cell to be modified.
- "value": the new value entered by the user, converted to the appropriate data type.
- "user_value": the raw string input entered by the user.
- "tz": the timezone to apply, if working with date or time values.
Below is an example of a callback function where the data property is a Pandas DataFrame, and the on_edit property is set to the edit_value() function:
def edit_value(state: State, var_name: str, payload):
state.data.at[payload["index"], payload["col"]] = payload["value"]
state.refresh(var_name)
Note that the State.refresh()
method must be explicitly called to update the display, as the
underlying state.data object remains unchanged, and Taipy GUI will not automatically detect the
modification.
There is an example on such a custom implementation of the on_edit callback function in this example, in this section.
Enumerated values¶
You can designate certain table columns as enumerated columns, requiring their values to be
selected from a predefined list. While Taipy GUI does not control the actual data stored in the
table, it can restrict user input during editing to ensure only valid values are entered.
When a user edits a cell, only values from the predefined list will be selectable.
To enable this, use the lov indexed property property for the specific column. You must create a list of allowed values for a column (e.g., "MyColumn") and assign this list to the lov[MyColumn] property.
Value restrictions
In most cases the user can select a value name from the predefined list. Only values from this
list are accepted in the cell.
However, in some cases, the predefined list should serve more as a recommendation rather than a
strict set of allowed values.
A common example would be a column intended to store color names. The application might provide a list of commonly used colors (e.g., red, orange, yellow, green, blue, and violet), but users may also want to enter other valid color names such as "rose" or "maroon."
To allow flexibility while still providing suggestions, you can set the first element of the lov list for that column to None. For example:
predefined_colors = [None, "Red", "Orange", "Yellow", "Green", "Blue", "Violet"]
Below is a complete example of this feature.
The rebuild property¶
When the application modifies the value of a dynamic property, the impact of the change is
immediately reflected on the application page. However, changing the value of properties that are
not dynamic requires that the user refreshes the page manually (or that the application
explicitly calls navigate()
with the force parameter set to True). This is due to the fact
that the entire front-end component must be entirely re-generated to reflect its new settings
based on the property values.
The table control provides the rebuild property that, when set to True, triggers
the rebuilding of the table front-end component and refreshes the page.
Note that this mechanism may hurt the user experience because rebuilding the entire component can
be a somewhat complex operation.
Here is a situation where you may need to use rebuild: your application displays
a table, and you want to provide a way to interactively change the order of its columns.
Here is what the application code would look like:
from taipy.gui import Gui
# x: [1..5]
x_range = range(1, 6)
data = {
"X": x_range,
"Y": [x*x for x in x_range]
}
column_orders = [("X;Y", "Squared"), ("Y;X", "Square root")]
columns = column_orders[0]
page = """
<|{data}|table|columns={columns[0]}|show_all|>
<|{columns}|toggle|lov={column_orders}|>
"""
Gui(page=page).run()
The table displays two columns of data, one column holding the square value of the other. When you run this application, here is what the page looks like:


A toggle button lets the user choose whether to represent, in the second column, the square of the
value in the first column or the other way around.
To implement this, the code is setting the value "X;Y" or "Y;X" to the property
columns.
Here is what the application looks like just after the user has changed the
value of the toggle button:


We can see that although the value for the toggle button was properly updated, the table has not rearranged its columns. That is because the columns property is not dynamic.
Setting the rebuild property to True allows for updating the table on the fly: let's change the table's Markdown definition to:
<|{data}|table|columns={columns[0]}|show_all|rebuild|>
If you run the application again, and select the alternative column order in the toggle button, here is what the page looks like:


Now the table properly reflects the value of the columns property and no manual refresh was needed.
Make sure, when you are using the rebuild property, that no performance impact is so bad that it would ruin the user experience.
Usage¶
Show tabular data¶
Suppose you want to display the data set defined as follows:
# x_range = [-10, -6, -2, 2, 6, 10]
x_range = range(-10, 11, 4)
data = {
"x": x_range,
# y1 = x*x
"y1": [x*x for x in x_range],
# y2 = 100-x*x
"y2": [100-x*x for x in x_range]
}
You can use the following control declaration to display all these numbers in a table:
Definition
<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")
The resulting image looks like this:


Large data¶
The example above had only six lines of data. If we change the x_range definition to create far more data lines, we come up with a table with much more data to display:
# large_x_range = [-10, -9.98, ..., 9.98, 10.0] - 1000 x values
large_x_range = [round(20*i/1000-10, 2) for i in range(0, 1001)]
data = {
"x": large_x_range,
# y1 = x*x
"y1": [round(x*x, 5) for x in large_x_range],
# y2 = 100-x*x
"y2": [round(100-x*x, 5) for x in large_x_range]
}
We can use the same table control definition:
Definition
<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")
To get a rendering looking like this:


Only the first 100 rows (as indicated in the 'Rows per page' selector) are visible.
The table scroll bar lets you navigate across the 100 first rows.
You can change how many rows are displayed simultaneously using the
page_size and page_size_options properties.
If you want to display all the rows at the same time, you can change the control definition to set the show_all to True:
Definition
<|{data}|table|show_all|>
<taipy:table show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", show_all=True)
Now the table displays all the data rows, and the scrollbar lets you navigate among all of them:


Setting the allow_all_rows property to True for a paginated table adds the 'All' option to the page size options, so the user can switch from one mode to the other.
Show specific columns¶
If you want to display a specific set of columns, you can use the columns property to indicate what columns should be displayed.
Here is how you would define the table control if you want to hide the column "y2" from the examples above:
Definition
<|{data}|table|columns=x;y1|>
<taipy:table columns="x;y1">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="x;y1")
And the y2 column is not displayed any longer:


Formatting values¶
The table
control has different means to let you specify what format to apply when displaying
numerical or date values:
- The number_format property specifies what format applies to all numerical
values in the table data.
The syntax for this property is defined by theformat()
method of the Pythonstring
type. - The date_format property specifies what format applies to all date values
in the table data.
The syntax for this property is defined by the JavaScriptdate-fns.format()
function.
In some situations, you need each column to be represented with a dedicated format definition.
When this happens, you can use the dictionary version of the columns property to
indicate the format to apply to each column, as described
above.
Consider the following data set:
stock = {
"date": [
datetime.datetime(year=2000, month=12, day=20),
datetime.datetime(year=2000, month=12, day=21),
datetime.datetime(year=2000, month=12, day=22),
...
]
"price": [119.88, 112.657, 164.5, 105.42, 188.36, 103.9, 143.97, 160.11, 136.3, 174.06],
"change": [7.814, -5.952, 0.01, 8.781, 7.335, 6.623, -6.635, -6.9, 0.327, -0.089],
"volume": [773, 2622, 2751, 1108, 7400, 3772, 9398, 4444, 9264, 1108],
}
Standard formatting requirements could be:
- The date values should be represented with only the month name and the day of the month.
The format to apply is "MMM d". - The price values must have two significative decimal places and hold the currency symbol.
The format to apply is "$%.02f": a dollar sign followed by the value with two decimal places. - The change values must have a single decimal place.
The format to apply is "%.01f": the value with one decimal place. - The title of each column must be capitalized.
We can force the column titles to: "Date", "Price", "% change" and "Volume"
The implementation of these requirements requires the creation of a columns dictionary defined as follows:
columns = {
"date": {"title": "Data", "format": "MMM d"},
"price": {"title": "Price", "format": "$%.02f"},
"change": {"title": "% change", "format": "%.01f"},
"volume": {"title": "Volume"}
}
We can use this dictionary in the table definition:
Definition
<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")
The resulting display reflects what is requested:


Custom formats¶
You can create a Python function to format the cell values according to the specific needs of your application. By assigning this function to the format_fn[column_name] property, the function will be used to format the cell values of that particular column when the table is displayed. This allows you to have entire control on how data are presented to the user.
Below is a simple example demonstrating this feature.
Suppose you are building an application that manages a table with user names and passwords. For
security reasons, when displaying the table, the passwords will be hidden (e.g., replaced with
asterisks).
However, you will allow the passwords to be visible during editing, where they will appear in plain
text.
Here is the dataset that we will use, stored in the variable data:
data = {
"User Name": ["johndoe", "janedoe", "admin", "sampleuser", "guestaccount" ],
"Password": ["Password123!", "Test@2023", "Admin#789", "SamplePass#1", "Guest!2024"]
}
Here is the definition of the table control:
Definition
<|{data}|table|format_fn[Password]=hide_text|editable[Password]|show_all|no on_add|no on_delete|>
<taipy:table format_fn[Password]="hide_text" editable[Password] show_all on_add="false" on_delete="false">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", format_fn__Password=hide_text, editable__Password=True, show_all=True, on_add=False, on_delete=False)
Here are the important property settings to mention:
- The format_fn property, for the "Password" column, is set to the hide_text() function (described below) to perform the masking of the text.
- The editable[Password] property is set to True, making only the "Password" column editable.
- The on_add and on_delete properties are both set to False, preventing the user from adding or deleting rows in the table.
Here is the formatting function:
def hide_text():
return "*" * 8
Here is what the table looks like:


If the user clicks the icon (Edit) next to the masked password, the text is revealed,
showing the actual password in plain text. This allows the user to view or modify the password, but
only when explicitly editing the field.
Here is what the user can see in this situation:


Pick from a list¶
Here is an example demonstrating how to use enumerated values to guide user input in a table.
As mentioned earlier, you need to set the lov indexed property for columns that should only accept values from a predefined list.
To illustrate this feature with an example, consider the following table:


This table contains a list of city names along with their respective country, continent, and population (in millions).
While the user can edit any cell, it would simplify editing and validation if the "Continent" column
were constrained to only accept valid continent names.
This is precisely the purpose of enumerated columns: To designate a column as enumerated, you must
set its lov indexed property to a list of allowed values..
In this example, we need to create a list of valid continent names and assign it to the
lov[Continent] property.
The Python code for defining the list of valid continents is:
all_continents = ["Africa", "America", "Antarctica", "Asia", "Europe", "Oceania"]
The table definition would then reference this all_continents variable:
Definition
<|{data}|table|lov[Continent]={all_continents}|editable|no on_add|no on_delete|show_all|>
<taipy:table lov[Continent]="{all_continents}" editable on_add="false" on_delete="false" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", lov__Continent="{all_continents}", editable=True, on_add=False, on_delete=False, show_all=True)
Note that all the cells in table can be edited because editable is set to
True.
However we prevent users from creating or deleting new rows by setting on_add and
on_delete to False.
When the user attempts to edit a cell in the "Continent" column, it will look like this:


A icon (Drop down) appears in the cell, which when clicked, displays a drop-down list of predefined values that the user can select from:


Action on edit¶
While the table control includes a built-in mechanism to automatically update the bound data object
(in the data property) when users edit the table (i.e., by adding or removing rows, or
modifying cell values), you can also define custom callback functions to handle or restrict these
changes.
This enables you to execute specific actions based on user modifications.
You can achieve this by setting the on_add, on_delete, or
on_edit on_edit properties.
Below is an example that demonstrates this
capability:
Suppose we are creating an application that displays how a user's assets are distributed across
various categories. The table would list each category alongside the allocated amount. The purpose
of the application is to show the percentage of each allocation relative to the user's total assets
and allow the user to modify the amount allocated to each category.
Any changes in the allocation will trigger a recalculation of the distribution across all
categories.
Below is the Python code that initializes the dataset with arbitrary initial values:
1 2 3 4 5 6 7 8 9 10 |
|
- categories holds the different asset categories.
- amounts holds the allocated amount for each category.
- ratio holds the percentage of each amount relative to the total assets.
The compute_ratio() function (defined on line 4) calculates these percentages and returns a list of integers representing the proportion of each amount.
The initial ratios are computed on line 8 and stored in the ratio variable. - The complete dataset is assembled in the data variable on line 10.
Here is the table control definition:
Definition
<|{data}|table|editable[Amount]|on_edit=update|no on_add|no on_delete|show_all|>
<taipy:table editable[Amount] on_edit="update" on_add="false" on_delete="false" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", editable__Amount=True, on_edit=update, on_add=False, on_delete=False, show_all=True)
Setting editable[Amount] to True makes the column editable.
Due to the configuration of on_edit, when the user modifies a value in the 'Amount'
column, the update() function is invoked.
To disable the ability to add or remove rows, the on_add and
on_delete properties are set to False.
This is how the page looks like initially:


Next, let's add functionality to update the "%" column when a user modifies the "Amount"
column.
Here is the definition of the update() function:
1 2 3 4 5 |
|
Finally, line 5 refreshes the var_name variable (set to "data") to update the display.
The requirements have now been implemented. Now, let's see how this works.
Assume the user enters a new value in a cell in the "Amount" column by pressing the icon
(Edit) next to the cell:


Now, suppose the user changes the value from 190,000 to 80,000 and confirms the change by pressing the icon (Apply). The on_edit setting triggers the invocation of the update() function, which in turn calls compute_ratio() to recalculate all ratios. Once the new ratios are stored in the "%" column, the page is refreshed to display the updated results:


As you can see, all the ratio values in the "%" column have been updated to reflect the impact of the new value.
Guarding edits¶
The on_edit callback is triggered when the content of a cell is edited by the user.
Your application may need to respond to this event, for instance, to perform computations or update
related data.
While you could use the on_change
callback to detect changes, doing so would not provide
contextual information about the specific cell that was modified, which may be useful in certain
scenarios.
In this example, we use the on_edit callback to address this limitation.
This property is set to a function that intercepts the cell edit operations.
We aim to build a small application that helps manage a team of employees. The requirements for the application are as follows:
- Team Size: The team should consist of three to six employees.
- Salary Adjustment: The salary for each employee should be rounded to the nearest multiple of $500.
The application interface is a table that displays the list of employees, with a column showing the proposed salary for each.
- The user can remove a candidate by pressing the icon (Delete) in the corresponding row, but the list must contain at least three employees.
- The user can add a new candidate to the list by pressing the icon (Add), but no more than six employees can be added.
- The user can edit any employee's salary, and the final amount is rounded to the nearest multiple of $500.
We begin by initializing the variable that stores the list of candidates. It is initially empty:
candidates = {"Name": [], "Salary": []}
Next, we define a function that selects a new candidate. The exact implementation of this function is not critical - it could retrieve data from a database or other sources containing candidate names and their salary expectations. The only important detail is that this function returns a tuple where the first element is the candidate’s name, and the second element is their initial salary proposal:
def pick_user() -> tuple[str, int]:
...
We can now populate the list with an initial set of four candidates:
for candidate in [pick_user() for _ in range(4)]:
candidates["Name"].append(candidate[0])
candidates["Salary"].append(candidate[1])
Now, we define our table component:
Definition
<|{candidates}|table|editable[Salary]|on_delete=check_delete|on_add=check_add|on_edit=force_salary|show_all|>
<taipy:table editable[Salary] on_delete="check_delete" on_add="check_add" on_edit="force_salary" show_all>{candidates}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{candidates}", editable__Salary=True, on_delete=check_delete, on_add=check_add, on_edit=force_salary, show_all=True)
Note that:
- The cells in the "Salary" column are editable.
- The on_add and on_delete properties are set to invoke functions that check for the number of candidates in the list before performing the operation.
- The on_edit property is set to invoke the force_salary() function.
Below is the initial state of the table when the application starts:


Removing rows¶
To control the behavior of the application when a row deletion is requested, we must define a callback function and assign it to the on_delete property of the table.
The following implementation ensures that at least three candidates remain in the candidates list before performing any deletion:
1 2 3 4 5 6 |
|
- In line 2, we store the current number of candidates (the number of rows in any column of state.candidates) in n_candidates.
- If there are three candidates or less (see line 3), a notification is triggered and the deletion is prevented.
- If more than three candidates remain, the deletion is allowed and is performed by the call to
the
table_on_delete()
method.
The parameters passed totable_on_delete()
are the same as those received by check_delete() since there is no need to modify them before forwarding the request.
Starting with the initial state (where four candidates are in the list), a row can be deleted by
clicking the icon (Delete) on the corresponding row, and then confirming the action by
pressing the icon (Apply).
Here is what the table look like after removing the third line:


There are now three candidates in the list. If you attempt to delete another row, the notification will appear, and the deletion will be blocked:


Adding rows¶
The on_add property is assigned to a function that controls how the application handles row additions. Below is the implementation of the callback function in this example:
1 2 3 4 5 6 7 8 |
|
- In line 2, we store the current number of candidates (the number of rows in a column of state.candidates) in n_candidates.
- If there are six candidates or more (see line 3), a notification is triggered and the addition of a new row is prevented.
- If there are less than six candidates, the creation of a new row is allowed.
- On line 6, a tuple with the new candidate's data is created using the pick_user() function.
- On line 7, payload["index"] is set to the current number of candidates to force the new row to be added at the end of the list (the default behavior is to add it at the top).
- On line 8, we invoke the
table_on_add()
method with the modified payload value, and the values for the new row, converted to alist
.
Assuming the application is in its initial state with four candidates, you can add a row by clicking
the icon (Add) at the top of the table control.
The result is displayed like this:


If we add another row then try to add yet another one, we get the notification:


Controlling cell value edits¶
The on_edit property allows you to control the values that users enter when modifying a cell's content. We can use this property to enforce the constraint that the salary must be rounded to the nearest multiple of 500.
Here is the implementation of the force_salary() function, which is assigned to the on_edit property of the table control in this example:
1 2 3 4 5 |
|
Let's explain what this function does line by line:
- Line 2: Retrieves the value entered by the user from the payload dictionary, sent by the
on_edit
callback. - Line 3: Rounds the value to the nearest multiple of 500, satisfying the constraint, and assign the result to proposed_salary.
- Line 4: Updates the "value" entry of the payload dictionary with the rounded salary to ensure the correct value is stored in the dataset.
- Line 5: Delegate the update of the dataset and the control by calling
table_on_edit()
.
Assume the user enters a random salary value for a candidate:


Obviously, the proposed value (963852) does not meet the constraint.
After the user validates the input (clicking the icon (Apply)), the force_salary() function is invoked to automatically adjusts the salary to the nearest multiple of 500, and the updated value is displayed:


Aggregation¶
To get the aggregation functionality in your table, you must indicate which columns can be aggregated and how to perform the aggregation.
This is done using the indexed group_by and apply properties.
The group_by[column_name] property, when set to True indicates that the column column_name can be aggregated.
The function provided in the apply[column_name] property indicates how to perform this aggregation. The value of this property, which is a string, can be:
- A built-in function. Available predefined functions are the following:
count
,sum
,mean
,median
,min
,max
,std
,first
(the default value), andlast
. - The name of a user-defined function or a lambda function.
This function receives a single parameter which is the series to aggregate, and it must return a scalar value that would result from the aggregation.
This control definition is all it takes to add aggregation functionality to the table:
Definition
<|{data}|table|group_by[Group column]|apply[Apply column]=count|>
<taipy:table group_by[Group column] apply[Apply column]="count">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", properties="{properties}")
Note
Note that we are using the properties property.
This is due to the fact that the column names cannot be used when expressing the indexed
property name, which would not be a valid Python identifier. The workaround is to create
a dictionary called properties that defines those property values:
properties = {
"group_by[Group column]": True,
"apply[Apply column]": "count"
}
Cell tooltips¶
You can specify a tooltip for specific table cells.
When Taipy creates the cells, it can add a specific tooltip that you would have set as the return value of the function set to the tooltip or tooltip[column_name] properties.
The signature of this function expects five optional parameters: - state: the current state. - value: the value of the cell. - index: the index of the row in this table. - row: all the values for this row. - column_name: the name of the column for this cell.
Based on these parameters, the function must return a string that defines a tooltip used as the cell's tooltip text.
Definition
<|{data}|table|tooltip={lambda state, val, idx: "A tooltip" if idx % 2 == 0 else "Another tooltip"}|>
<taipy:table tooltip="{lambda state, val, idx: 'A tooltip' if idx % 2 == 0 else 'Another tooltip'}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", tooltip=lambda state, val, idx: "A tooltip" if idx % 2 == 0 else "Another tooltip")
Styling¶
All the table controls are generated with the "taipy-table" CSS class. You can use this class name to select the tables on your page and apply style.
Stylekit support¶
The Stylekit provides a CSS custom property:
- --table-stripe-opacity
This property contains the opacity applied to odd lines of tables.
The default value is 0.5.
The Stylekit also provides specific CSS classes that you can use to style tables:
- header-plain
Adds a plain and contrasting background color to the table header. - rows-bordered
Adds a bottom border to each row. - rows-similar
Removes the even-odd striped background so all rows have the same background.
Dynamic styling¶
You can modify the style of entire rows or specific table cells based on any criteria, including the table data itself.
When Taipy creates the rows and the cells, it can add a specific CSS class to the generated elements. This class name is the string returned by the function set to the row_class_name property for entire rows, or cell_class_name[column_name] for specific cells.
The signature of this function depends on which property you use:
- row_class_name: this applies to entire rows.
The given function expects three optional parameters:- state: the current state
- index: the index of the row in this table
- row: all the values for this row
- cell_class_name[column_name]: this applies to specific
cells.
The given function expects five optional parameters:- state: the current state
- value: the value of the cell
- index: the index of the row in this table
- row: all the values for this row
- column_name: the name of the column for this cell
Based on these parameters, the function must return a string that defines a CSS class name that will
be added to the CSS classes for this table row or this specific cell.
The example below shows how this works.
Styling rows¶
To apply a specific CSS class to a table row, you will use the row_class_name
property (note that you can find general instructions on how to apply style to visual elements in
this page).
This property holds a function that is invoked when each row is rendered, and it must return
the name of a class, defined in CSS.
Here is how a row styling function can be defined:
def even_odd_class(_1, row):
if row % 2:
return "blue-row"
else:
return "red-row"
We only use the second parameter since, in this straightforward case, we do not need the application
state (first parameter) or the values in the row (third parameter).
Based on the row index (received in index), this function returns the name of the CSS class to
apply to the row: "blue-row" if the index is odd, "red-row" if it is even.
We need to define what these class define. This is done in a CSS style sheet, where the following CSS content would appear:
.blue-row>td {
color: white;
background-color: blue;
}
.red-row>td {
color: yellow;
background-color: red;
}
Note that the CSS selectors use the child combinator selector ">" to target elements that hold a
td
element (the cells themselves).
To use this style, we can adjust the control definition used above so it looks like this:
Definition
<|{data}|table|row_class_name=even_odd_class|>
<taipy:table row_class_name="even_odd_class">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", row_class_name=even_odd_class)
The resulting display will be what we expected:


The "red-row" CSS class applies to every even row and similarly the "blue-row" CSS class for odd rows.
Note that the styling function is so simple that we could have made it a lambda, directly in the control definition:
Alternative definition
<|{data}|table|row_class_name={lambda _, row: "blue-row" if row % 2 else "red-row"}|show_all|>
<taipy:table data="{data}" row_class_name="{lambda _, row: 'blue-row' if row % 2 else 'red-row'}" show_all/>
import taipy.gui.builder as tgb
...
tgb.table("{data}", row_class_name=lambda _, row: "blue-row" if row % 2 else "red-row", show_all=True)
This definition of the control makes the even_odd_class() function useless.
Styling cells¶
To change the style of specific cells, you must use the cell_class_name[] indexed property. This property should be set to a function that returns a CSS class name, allowing dynamic styling based on the parameters passed to the function. These parameters are:
- The value of the cell,
- The index of the row,
- The value of another column in the same row,
- The name of the column.
Here is an example of how to use this property.
Consider a table with three columns:
- Column "x" contains integers from 0 to 10.
Requirement: Cells in the "x" column should have a background color alternating between two shades of green. - Column "y" contains the square of the corresponding values in column "x".
Requirement: Cells in the "y" column should have a background color alternating between two shades of red. - Column "z" contains random values between 0 and 5.
Requirement: Cells in the "z" column should have a background color that varies based on the value: the smaller the value, the lighter the shade of blue.
Here are the CSS classes that this application will use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
- The "reddish" and "light-reddish" classes (lines 1 to 13) will apply to the "x" column. The "reddish" class has a strong red background and uses white text, while the "light-reddish" class uses a lighter red background with black text and bold typeface.
- The "greenish" and "light-greenish" classes (lines 15 to 28) will apply to the "y" column. The "greenish" class has a green background with white text, while the "light-greenish" class has a lighter green background with black text and bold typeface.
- The "col0" to "col5" classes (lines 29 to 58) define the background color for the "z" column. These classes create a gradient from light gray ("col0") to dark blue ("col5"). For classes with darker backgrounds (from "col3" to "col5"), white text is used for better readability.
Below is how we configure the table control:
Definition
<|{data}|table|cell_class_name[x]=xy_class|cell_class_name[y]=xy_class|cell_class_name[z]=z_class|show_all|>
<taipy:table cell_class_name[x]="xy_class" cell_class_name[y]="xy_class" cell_class_name[z]="z_class" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", cell_class_name__x=xy_class, cell_class_name__y=xy_class, cell_class_name__z=z_class, show_all=True)
Both cell_class_name[x] and cell_class_name[y] are set to the xy_class() function defined
below.
cell_class_name[z] is set to z_class(), which functions differently to meet specific requirements.
Here is the definition for xy_class() and z_class(), used in the control definition:
def xy_class(_1, _2, index, _3, column_name):
return (
("greenish" if index % 2 else "light-greenish")
if column_name == "x"
else ("reddish" if index % 2 else "light-reddish")
)
def z_class(_, value):
return f"col{value}"
xy_class() uses the column name (passed as the column_name parameter) and the row index (passed as the index parameter) to determine the CSS class for each cell:
- If the column is "x", it applies either the "greenish" or "light-greenish" class, alternating based on the row index (even or odd).
- For the column "y" (
else
case), it applies either the "reddish" or "light-reddish" class, also alternating based on the row index.
z_class() dynamically builds a CSS class name based on the value of the cell, using the value parameter. For example, if the value is 2, it will return "col2".
The result is a table where each cell is styled with the appropriate CSS class based on its location or value, ensuring consistent and logical styling across the table:


Note that the implementation of z_class() is simple enough that it could be replaced with a lambda function directly in the control definition:
Definition
<|{data}|table|cell_class_name[x]=xy_class|cell_class_name[y]=xy_class|cell_class_name[z]={lambda _, v: f'col{v}'}|show_all|>
<taipy:table cell_class_name[x]="xy_class" cell_class_name[y]="xy_class" cell_class_name[z]="{lambda _, v: f'col{v}'}" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", cell_class_name__x=xy_class, cell_class_name__y=xy_class, cell_class_name__z=lambda _, v: f'col{v}', show_all=True)
In this case, the z_class() function would no longer be needed.