• English
  • ProTable - Advanced Table

    ProTable was created to solve the problem of having to write a lot of sample code for tables in a project, so a lot of common logic was encapsulated in it. These wrappers can be simply categorized as pre-defined behaviors and pre-defined logic.

    Built on ProForm, ProTable can switch query UI modes (for example full QueryFilter vs light filter) and also behave more like a plain form when using type="form".

    layout

    When to Use

    When your tables need to interact with the server or need rich cell renderers, ProTable is a good fit.

    Issue regression scenarios

    ID名称
    暂无数据
    暂无数据
    State inspection and editable drag sorting
    import type {
      ActionType,
      EditableFormInstance,
    } from '@ant-design/pro-components';
    import {
      EditableProTable,
      ProCard,
      ProTable,
    } from '@ant-design/pro-components';
    import { Alert, Button, Space, Tabs, Typography, message } from 'antd';
    import { useRef, useState } from 'react';
    
    type Row = {
      id: number;
      name: string;
      status?: 'active' | 'closed';
      sort?: number;
    };
    
    const rows: Row[] = [
      { id: 1, name: 'Alpha', status: 'active' },
      { id: 2, name: 'Beta', status: 'closed' },
      { id: 3, name: 'Gamma', status: 'active' },
    ];
    
    const CursorPagination = () => (
      <ProTable<Row>
        rowKey="id"
        search={false}
        options={false}
        columns={[
          { title: 'ID', dataIndex: 'id' },
          { title: '名称', dataIndex: 'name' },
        ]}
        pagination={{ type: 'cursor', pageSize: 1 }}
        request={async ({ nextToken }) => {
          const index = Number(nextToken ?? 0);
          return {
            success: true,
            data: rows.slice(index, index + 1),
            nextToken: index + 1 < rows.length ? String(index + 1) : undefined,
          };
        }}
      />
    );
    
    const SortFilterState = () => {
      const actionRef = useRef<ActionType>();
      const [snapshot, setSnapshot] = useState('点击按钮读取');
      return (
        <ProTable<Row>
          actionRef={actionRef}
          rowKey="id"
          search={false}
          pagination={false}
          dataSource={rows}
          columns={[
            { title: '名称', dataIndex: 'name', sorter: true },
            {
              title: '状态',
              dataIndex: 'status',
              filters: true,
              valueEnum: { active: '启用', closed: '关闭' },
            },
          ]}
          toolBarRender={() => [
            <Space key="state">
              <Button
                onClick={() =>
                  setSnapshot(
                    JSON.stringify(actionRef.current?.getSortFilter?.() ?? {}),
                  )
                }
              >
                读取排序和筛选
              </Button>
              <Typography.Text code>{snapshot}</Typography.Text>
            </Space>,
          ]}
        />
      );
    };
    
    const EditableDragSort = () => {
      const [dataSource, setDataSource] = useState<Row[]>(rows);
      return (
        <EditableProTable<Row>
          rowKey="id"
          value={dataSource}
          onChange={(value) => setDataSource([...value])}
          columns={[
            { title: '排序', dataIndex: 'sort', width: 64 },
            {
              title: '名称',
              dataIndex: 'name',
              formItemProps: {
                rules: [{ required: true, message: '请填写名称' }],
              },
              errorType: 'default',
            },
          ]}
          recordCreatorProps={false}
          dragSortKey="sort"
          editable={{ type: 'multiple', editableKeys: [1] }}
          onDragSortEnd={(_, __, nextDataSource) => {
            setDataSource(nextDataSource);
            message.success('顺序已更新,编辑行保持编辑状态');
          }}
        />
      );
    };
    
    const InlineValidation = () => {
      const editableFormRef = useRef<EditableFormInstance<Row>>();
      return (
        <Space direction="vertical" style={{ width: '100%' }}>
          <Button
            onClick={() => {
              editableFormRef.current?.validateFields().catch(() => undefined);
            }}
          >
            验证并显示行内错误
          </Button>
          <EditableProTable<Row>
            editableFormRef={editableFormRef}
            rowKey="id"
            value={[{ id: 1, name: '' }]}
            recordCreatorProps={false}
            columns={[
              {
                title: '名称',
                dataIndex: 'name',
                errorType: 'default',
                formItemProps: {
                  rules: [{ required: true, message: '请填写名称' }],
                },
              },
            ]}
            editable={{ type: 'multiple', editableKeys: [1] }}
          />
        </Space>
      );
    };
    
    export default () => (
      <ProCard direction="column" ghost gutter={[0, 16]}>
        <Alert
          type="info"
          showIcon
          message="表格稳定性与新增能力"
          description="覆盖游标分页、排序筛选状态、可编辑行拖拽和行内校验错误。"
        />
        <ProCard>
          <Tabs
            destroyOnHidden
            items={[
              {
                key: 'cursor',
                label: '游标分页 #9500',
                children: <CursorPagination />,
              },
              {
                key: 'state',
                label: '排序筛选 #9197',
                children: <SortFilterState />,
              },
              {
                key: 'drag',
                label: '编辑拖拽 #9046',
                children: <EditableDragSort />,
              },
              {
                key: 'error',
                label: '行内错误 #8786',
                children: <InlineValidation />,
              },
            ]}
          />
        </ProCard>
      </ProCard>
    );
    

    API

    ProTable puts a layer of wrapping on top of antd's Table, supports some presets, and encapsulates some behaviors. Only APIs that differ from antd Table are listed here.

    request

    request is the most important API of ProTable, request takes an object. The object must have data and success in it, and total is also required if manual paging is needed. request takes over the loading settings and re-executes them when the query form is queried and the params parameters are modified. Also the query form values and params parameters are brought in. The following is an example.

    <ProTable<DataType, Params>
      // params is a parameter that needs to be self-contained
      // This parameter has higher priority and will override the parameters of the query form
      params={params}
      request={async (
        // The first parameter params is the combination of the query form and params parameters
        // The first parameter will always have pageSize and current, which are antd specifications
        params: T & {
          pageSize: number;
          current: number;
        },
        sort,
        filter,
      ) => {
        // Here you need to return a Promise, and you can transform the data before returning it
        // If you need to transform the parameters you can change them here
        const msg = await myQuery({
          page: params.current,
          pageSize: params.pageSize,
        });
        return {
          data: msg.result,
          // Please return true for success.
          // otherwise the table will stop parsing the data, even if there is data
          success: boolean,
          // not passed will use the length of the data, if it is paged you must pass
          total: number,
        };
      }}
    />

    request is also supported in column configuration, but only for a few valueType.

    ProTable

    PropertyDescriptionTypeDefault Value
    columnsColumn configuration capability, supports an arrayProColumns<T, ValueType>[]-
    requestHow to get dataSource(params: U & {pageSize?: number, current?: number, keyword?: string}, sort: Record<string, SortOrder>, filter: Record<string, FilterValue>) => Promise<{data: T[], success?: boolean, total?: number}>-
    paramsAdditional parameters used for request query, once changed will trigger reloadingU-
    pollingWhether to poll, polling indicates the polling interval, 0 to close polling, greater than 0 to open polling, minimum polling time is 2000msnumber | ((dataSource: T[]) => number) | undefined-
    postDataProcess the data obtained through request(data: T[]) => T[]-
    defaultDataDefault dataT[]-
    dataSourceTable data, ProTable recommends using request to loadT[]-
    onDataSourceChangeTriggered when Table data changes(dataSource: T[]) => void-
    paginationConfiguration of pagination. current and pageSize will be taken over by requestTablePaginationConfig | false-
    actionRefReference to Table action for custom triggeringReact.Ref<ActionType | undefined>-
    formRefThe form instance of the query form can be obtained for some flexible configurationTableFormItem<T>['formRef']-
    toolBarRenderRender toolbar, support returning a dom array, will automatically increase margin-rightToolBarProps<T>['toolBarRender'] | false-
    optionsRenderCustom render toolbar optionsToolBarProps<T>['optionsRender']-
    onLoadTriggered after the data is loaded, it will be triggered multiple times(dataSource: T[]) => void-
    onLoadingChangeTriggered when loading is modified, usually caused by network requests(loading: boolean | SpinProps | undefined) => void-
    onRequestErrorTriggered when data loading fails(error: Error) => void-
    tableClassNameclassName of the encapsulated tablestring-
    tableStylestyle of the encapsulated tableCSSProperties-
    headerTitleThe title of the top-left cornerReactNode-
    tooltipTooltip next to the titlestring | LabelTooltipType-
    optionstable toolbar, not displayed when set to false, passing function will trigger when clicked{{ density?: boolean, fullScreen?: boolean | function, reload?: boolean | function, reloadIcon?: React.ReactNode, densityIcon?: React.ReactNode, setting?: boolean | SettingOptionType &#125;&#125;{ reload: true, density: true, setting: true, search: false, fullScreen: false }
    searchWhether to display the search form, when the object is passed in, it is the configuration of the search formfalse | SearchConfig-
    defaultSizeDefault sizeSizeType-
    dateFormatterConvert dayjs format data to a specific type, false will not be converted"string" | "number" | ((value: dayjs.Dayjs, valueType: string) => string | number) | false"string"
    beforeSearchSubmitMake some changes before searching(params: Partial<U>) => any-
    onSizeChangeThe table size has changed(size: DensitySize) => void-
    typepro-table typeProSchemaComponentTypes-
    formtype="form" and Form configuration of search form. Set ignoreRules: false to enable search form validation, see detailsOmit<ProFormProps & QueryFilterProps, 'form'>-
    onSubmitTriggered when the form is submitted(params: U) => void-
    onResetTriggered when the form is reset() => void-
    columnEmptyTextDisplay when it is empty, display - when it is not set, false can turn off this functionProFieldEmptyText-
    tableRenderCustom rendering table function(props: ProTableProps<T, U, ValueType>, defaultDom: ReactNode, domList: {toolbar: ReactNode, alert: ReactNode, table: ReactNode}) => ReactNode-
    tableViewRenderRender table view, used for customizing ProList, not recommended for direct use; defaultDom may be a lazy factory — call when the default Table is needed (see resolveTableViewDefaultDom)(props: TableProps<DataSource>, defaultDom: JSX.Element | (() => JSX.Element)) => JSX.Element | undefined-
    searchFormRenderRender search form(props: ProTableProps<T, U, ValueType>, defaultDom: JSX.Element) => ReactNode-
    toolbarTransparent transmission of ListToolBar configuration itemsListToolBarProps-
    tableExtraRenderdom rendering between table and search form(props: ProTableProps<T, U, ValueType>, dataSource: T[]) => ReactNode-
    manualRequestDo you need to manually trigger the first request? When configured as true, the search form cannot be hiddenbooleanfalse
    editableRelated configuration of editable table. DetailsRowEditableConfig<T>-
    cardPropsSettings for the card outside the tableProCardProps | false-
    cardBorderedBorder of Card components around Table and Searchboolean | &#123;search?: boolean, table?: boolean&#125;false
    ghostGhost mode, that is, whether to cancel the padding of the table content area.booleanfalse
    debounceTimeDebounce timenumber20
    revalidateOnFocusAutomatically re-request when the window is focusedbooleanfalse
    columnsStateColumn Status Control, you can operate the display hideColumnStateType-
    nameThe name of the editable table, through which you can communicate directly with the form without nestingNamePath-
    ErrorBoundaryComes with error handling function to prevent blank screen. ErrorBoundary=false turn off default ErrorBoundaryReact.ComponentClass<any, any> | falseBuilt-in ErrorBoundary
    styleStyle of the outer containerReact.CSSProperties-
    rowSelectionSelection configurationTableProps<T>['rowSelection'] & {alwaysShowAlert?: boolean} | false-

    RecordCreator (EditableProTable)

    PropertyDescriptionTypeDefault Value
    (...buttonProps)ButtonProps of antdButtonProps—
    recordThe row data to be added, generally contains a unique key. Supports a function ((index, dataSource) => T)T | ((index: number, dataSource: T[]) => T){}
    positionWhere does the line increase, start or end'top' | 'bottom''bottom'
    newRecordTypeType of the new row: dataSource appends to data directly, cache puts it in cache and disappears on cancel'dataSource' | 'cache'-
    parentKeyWhich node to add to, generally for multiply nested tablesReact.Key | ((index: number, dataSource: T[]) => React.Key)-

    ColumnStateType

    PropertyDescriptionTypeDefault
    defaultValueThe default value of the column status, only for the first time. Used for resetting valueRecord<string, ColumnsState>;-
    valueColumn status, support controlled modeRecord<string, ColumnsState>;-
    onChangeTriggered after the column status value changes(value:Record<string, ColumnsState>)=>void-
    persistenceKeyThe key of the persistence column is used to determine if it is the same tablestring | number-
    persistenceTypeThe type of persistence column, localStorage is also existing after closing the browser, sessionStorage closes the browser will be lostlocalStorage | sessionStorage-

    Search Search form

    PropertyDescriptionTypeDefault Value
    filterTypeFilter form type'query' | 'light''query'
    searchTextSearch button textstringSearch
    resetTextreset button textstringReset
    labelWidthLabel widthnumber | 'auto'80
    spanConfigure the number of columns in the query form, supports breakpoint config (xs/sm/md/lg/xl/xxl); responsive defaults when unsetnumber | SpanConfigResponsive defaults
    classNameEncapsulated search Form classNamestring-
    collapseRenderCollapse button render((collapsed: boolean,showCollapseButton?: boolean) => ReactNode)|false-
    defaultCollapsedWhether to collapse by defaultbooleantrue
    collapsedWhether collapsedboolean-
    onCollapseCollapse button event(collapsed: boolean) => void;-
    optionRenderCustom action bar((searchConfig,formProps,dom) => ReactNode[])|false-
    showHiddenNumWhether to show the number of hidden items after storingbooleanfalse

    optionRender: false only hides the action buttons. To query after an input or selection changes, call formRef.current?.submit() from form.onValuesChange; use ProTable's debounceTime to debounce requests.

    Rules validation in the search form

    To avoid blocking searches, the search form ignores validation rules from column-level formItemProps.rules by default (ProTable injects ignoreRules: true automatically). To enable validation in the search form, set it explicitly:

    <ProTable
      columns={columns}
      // enable search form validation
      form={{ ignoreRules: false }}
    />

    Once enabled, formItemProps.rules (such as required) take effect when submitting the search; a failed validation blocks the search request. Row-level validation in the editable table (editable) is not affected by this config.

    ColConfig

    const defaultSpanConfig = {
      xs: 24,
      sm: 24,
      md: 12,
      lg: 12,
      xl: 8,
      xxl: 6,
    };

    Actual breakpoints come from antd design tokens (screenSMMin etc.) and can be customized via ConfigProvider theme; values above are defaults under default tokens.

    export type OptionsType =
      | ((e: React.MouseEvent<HTMLSpanElement>, action?: ActionType) => void)
      | boolean;
    
    export type OptionConfig = {
      density?: boolean;
      fullScreen?: OptionsType;
      reload?: OptionsType;
      setting?: boolean | SettingOptionType;
      search?: (OptionSearchProps & { name?: string }) | boolean;
      reloadIcon?: React.ReactNode;
      densityIcon?: React.ReactNode;
    };
    
    export type SettingOptionType = {
      draggable?: boolean;
      checkable?: boolean;
      showListItemOption?: boolean;
      checkedReset?: boolean;
      listsHeight?: number;
      extra?: React.ReactNode;
      children?: React.ReactNode;
      settingIcon?: React.ReactNode;
      /** #9620: Custom title render for column setting list items, e.g. remove fixed width for adaptive single-line display */
      listItemTitleRender?: (
        title: React.ReactNode,
        column: { key?: React.Key; title?: React.ReactNode; [key: string]: any },
      ) => React.ReactNode;
    };

    ActionRef manually triggered

    Sometimes we need to manually trigger the reload of the table and other operations, we can use actionRef.

    MethodDescriptionType
    reloadRefresh the table, if true is passed, reset the page number(resetPageIndex?: boolean) => Promise<void>
    reloadAndRestRefresh and clear, the page number will also be reset, excluding the form() => Promise<void>
    resetReset to default values, including forms() => void
    clearSelectedClear the selected item() => void
    startEditableStart editing row(rowKey: Key) => boolean
    cancelEditableCancel editing row(rowKey: Key) => boolean
    scrollToScroll to specified position(arg: number | &#123; index?: number; key?: Key; top?: number &#125;) => void
    fullScreenSwitch full screen() => void
    setPageInfoSet page information(page: Partial<PageInfo>) => void
    // Example
    const ref = useRef<ActionType>();
    
    <ProTable actionRef={ref} />;
    
    // refresh
    ref.current?.reload();
    
    // Refresh and clear, the page number will also be reset, excluding the form
    ref.current?.reloadAndRest();
    
    // Reset to default values, including forms
    ref.current?.reset();
    
    // Clear the selected item
    ref.current?.clearSelected();
    
    // start editing
    ref.current?.startEditable(rowKey);
    
    // end editing
    ref.current?.cancelEditable(rowKey);

    Columns column definition

    Requesting remote data is more complicated, please see here for details.

    💡 Local sort/filter with request When you provide request, ProTable will trigger a reload on sort/filter changes and pass server-related sort / filter into request(params, sort, filter) by default.

    If you want client-side sorting/filtering (and do not want to trigger request):

    • Local filter: configure both filters and onFilter (a function or true).
    • Local sort: set sorter to a compare function, or { compare }. Avoid sorter: true (it means server-side sorting). :::
    PropertyDescriptionTypeDefault Value
    titleBasically the same as in antd, but supports passing in a methodReactNode | ((config: ProColumnType<T>, type: ProTableTypes) => ReactNode)-
    tooltipAn icon will be displayed after the title, and some information will be prompted after hoverLabelTooltipType | string-
    ellipsisWhether to abbreviate automatically. true uses antd Table native CSS ellipsis (better performance, no hover tooltip); pass {showTitle?: boolean, tooltip?: TooltipProps} for Typography rendering with tooltipboolean | {showTitle?: boolean}-
    copyableWhether to support copyingboolean-
    valueEnumThe value enumeration will automatically convert the value as a key to retrieve the content to be displayedvalueEnum-
    valueTypeThe type of value, which will generate different renderersvalueTypetext
    orderThe weight in the query form, the weight is ranked firstnumber-
    fieldPropsThe props of the query form will be transparently transmitted to the form item. If it is rendered as Input, all props of input are supported. Similarly, if it is select, all props of select are also supported. Also supports method input(form,config)=>Record | Record-
    formItemPropsThe configuration passed to Form.Item can be configured with rules, but the search form ignores rules by default. Set form={{ ignoreRules: false }} to enable them(form,config)=>formItemProps | formItemProps-
    renderTextRender like table, but must return string. If you just want to convert enumeration, you can use valueEnum(text: any,record: T,index: number,action: UseFetchDataAction<T>) => string-
    renderRender similar to table, the first parameter becomes dom, and the fourth parameter action is added(text: ReactNode,record: T,index: number,action: UseFetchDataAction<T>) => ReactNode | ReactNode[]-
    formItemRenderRender the input components of the query form(item,{ type, defaultRender, formItemProps, fieldProps, ...rest },form,action) => ReactNode-
    searchConfiguration column search related, false is hiddenboolean | { transform: (value: any) => any }true
    sorterBasically same as antd, newly added support for string override this field when requesting fieldfunction | boolean | string | &#123; compare: function, multiple: number &#125;-
    search.transformThe key of the conversion value, generally used for the conversion of the event interval(value: any) => any-
    editableWhether it is editable in the edit table, the parameters of the function are the same as the render of the tableboolean | (text: any, record: T,index: number) => boolean-
    colSizeThe number of grids occupied by a form item, proportion = colSize*span, colSize defaults to 1, span is 8, span is form={{span:8}} global setting Ofnumber-
    hideInTableDo not show this column in Tableboolean-
    hideInFormDo not show this column in Formboolean-
    hideInDescriptionsDo not show this column in Descriptionsboolean-
    hideInSettingDo not display in configuration toolboolean-
    filtersThe filter menu item in the header. When the value is true, valueEnum is automatically generatedboolean | object[]false
    onFilterFilter the form, use the built-in ProTable when it is true, turn off local filtering when it is false(value, record) => boolean | falsefalse
    onCellSame as antd Table. Set cell props; returning colSpan / rowSpan merges cells (0 hides the cell), identical to the antd colspan/rowspan demo(record: T, index?: number) => React.TdHTMLAttributes<HTMLTableCellElement>-
    requestRequest enumeration from serverrequest-
    initialValueInitial value of query form itemany-
    disableStatus of disabled in column settingsboolean | { checkbox: boolean; }-
    readonlyread onlyboolean-
    listSlotList slot (ProList slot), maps this column to a slot of the list item, e.g. title, avatar, description, subTitle, content, actions, aside, type. Private propertystring-

    valueType value type

    ProTable encapsulates some commonly used value types to reduce repeated render operations. Configure a valueType to display formatted response data.

    Batch operation

    Like antd, batch operations need to be set to "rowSelection" to enable. Unlike antd, pro-table provides an alert to carry some information. You can customize it with tableAlertRender and tableAlertOptionRender. Set or return false to close.

    PropertyDescriptionTypeDefault Value
    alwaysShowAlertAlways show alert, no choice not to show by default (rowSelection built-in attribute)boolean-
    tableAlertRenderCustomize the information area on the left side of the batch operation toolbar, not displayed when false({ selectedRowKeys: Key[], selectedRows: T[], onCleanSelected: ()=>void }) => ReactNode)|false-
    tableAlertOptionRenderCustomize the option area on the right side of the bulk operation toolbar, not displayed when false({ selectedRowKeys: Key[], selectedRows: T[], onCleanSelected: ()=>void }) => ReactNode)|false-

    Searching for forms

    ProTable will generate a Form for filtering list data based on columns, and the final value will be returned based on the first parameter via request, which looks like.

    <ProTable request={(params,sort,filter)=>{all params}}>

    As per the specification, table forms do not require any mandatory parameters, and all clicks on search and reset will trigger a request to initiate a query.

    Form's columns are generated with different types based on valueType.

    Columns with a valueType of index indexBorder option and no dataIndex and key will be ignored.

    ListToolbar

    Toolbar section for customizing forms.

    ListToolBarProps

    Toolbar configuration properties for lists and tables

    ParametersDescriptionTypeDefault
    titletitleReactNode-
    subTitlesubTitleReactNode-
    tooltiptooltip Descriptionstring | LabelTooltipType-
    searchquery area, supports Input.Search config, custom node or false to hideSearchProps & {onSearch} | ReactNode | boolean-
    onSearchTriggered when search is performed(keyWords: string) => void-
    actionsactions areaReactNode[]-
    settingssettings area(ReactNode | Setting)[]-
    filterThe filter area, usually used with LightFilterReactNode-
    multipleLineWhether to display multiple linesbooleanfalse
    menumenu configurationListToolBarMenu-
    tabsTabs configuration, only valid if multipleLine is trueListToolBarTabs-

    SearchProps is a property of antd's Input.Search.

    Setting

    ParametersDescriptionTypeDefault
    iconiconReactNode-
    tooltiptooltip DescriptionLabelTooltipType | string-
    keyoperation unique identifierstring-
    onClickset to be triggered(key: string)=>void-

    ListToolBarMenu

    ParametersDescriptionTypeDefault
    typetypeinline | dropdown | tabinline
    activeKeycurrent valueReact.Key-
    itemsmenu items{ key: React.Key; label: ReactNode; disabled?: boolean }[]-
    onChangeCallback for switching menus(activeKey: React.Key)=>void-

    ListToolBarTabs

    ParametersDescriptionTypeDefault
    activeKeycurrently selected itemstring-
    itemsmenu itemsTabPaneProps & { key?: string }[]-
    onChangeCallback for toggling menu items(activeKey)=>void-

    TableDropdown

    ParametersDescriptionTypeDefault
    menusMenu configuration{ key: string; name: ReactNode; disabled?: boolean }[]-
    onSelectCallback for selecting menu(key: string) => void-