Skip to main content
Get up and running with ProComponents by building your first data table and form in just a few minutes.

Prerequisites

Before you begin, make sure you have:
  • React 18+ installed
  • Ant Design 6+ installed
  • ProComponents installed (see Installation)

Your first ProTable

Let’s build a data table that fetches GitHub issues and displays them with search and filtering capabilities.
1

Create the table component

Create a new file IssueTable.tsx and import the required components:
import { ProTable } from '@ant-design/pro-components';
import type { ProColumns } from '@ant-design/pro-components';
import { Tag, Space } from 'antd';
2

Define your data type

Define the TypeScript interface for your data:
type GithubIssue = {
  id: number;
  number: number;
  title: string;
  state: string;
  labels: {
    name: string;
    color: string;
  }[];
  created_at: string;
  updated_at: string;
};
3

Configure the columns

Define your table columns with ProComponents’ powerful column types:
const columns: ProColumns<GithubIssue>[] = [
  {
    dataIndex: 'index',
    valueType: 'indexBorder',
    width: 48,
  },
  {
    title: 'Title',
    dataIndex: 'title',
    copyable: true,
    ellipsis: true,
    tooltip: 'The issue title',
  },
  {
    title: 'State',
    dataIndex: 'state',
    valueType: 'select',
    valueEnum: {
      open: { text: 'Open', status: 'Error' },
      closed: { text: 'Closed', status: 'Success' },
    },
  },
  {
    title: 'Labels',
    dataIndex: 'labels',
    search: false,
    render: (_, record) => (
      <Space>
        {record.labels.map(({ name, color }) => (
          <Tag color={color} key={name}>{name}</Tag>
        ))}
      </Space>
    ),
  },
  {
    title: 'Created',
    dataIndex: 'created_at',
    valueType: 'dateTime',
    sorter: true,
  },
];
4

Add the request function

ProTable automatically handles pagination and fetching:
const IssueTable = () => {
  return (
    <ProTable<GithubIssue>
      columns={columns}
      request={async (params, sort, filter) => {
        const response = await fetch(
          `https://api.github.com/repos/ant-design/ant-design/issues?` +
          new URLSearchParams({
            page: String(params.current),
            per_page: String(params.pageSize),
            state: params.state || 'open',
          })
        );
        const data = await response.json();
        return {
          data: data,
          success: true,
          total: 1000,
        };
      }}
      rowKey="id"
      pagination={{
        defaultPageSize: 10,
      }}
      search={{
        labelWidth: 'auto',
      }}
      dateFormatter="string"
      headerTitle="GitHub Issues"
    />
  );
};

export default IssueTable;
ProTable automatically generates a search form based on your column definitions. Columns with search: false won’t appear in the search form.

Your first ProForm

Now let’s create a form with validation and multiple field types.
1

Import form components

import { ProForm, ProFormText, ProFormSelect, ProFormDatePicker } from '@ant-design/pro-components';
import { message } from 'antd';
2

Create the form

const UserForm = () => {
  return (
    <ProForm
      onFinish={async (values) => {
        console.log('Form values:', values);
        message.success('Submitted successfully!');
        return true;
      }}
      initialValues={{
        role: 'user',
      }}
    >
      <ProFormText
        name="name"
        label="Name"
        placeholder="Please enter name"
        rules={[{ required: true, message: 'Name is required' }]}
      />
      
      <ProFormText
        name="email"
        label="Email"
        placeholder="Please enter email"
        rules={[
          { required: true, message: 'Email is required' },
          { type: 'email', message: 'Invalid email format' },
        ]}
      />
      
      <ProFormSelect
        name="role"
        label="Role"
        options={[
          { label: 'Admin', value: 'admin' },
          { label: 'User', value: 'user' },
          { label: 'Guest', value: 'guest' },
        ]}
        rules={[{ required: true }]}
      />
      
      <ProFormDatePicker
        name="birthday"
        label="Birthday"
      />
    </ProForm>
  );
};

Form layouts

ProComponents provides multiple form layouts for different use cases:

Complete example

Here’s a complete working example combining ProTable with form actions:
import { ProTable, ModalForm, ProFormText } from '@ant-design/pro-components';
import type { ProColumns, ActionType } from '@ant-design/pro-components';
import { Button, message } from 'antd';
import { useRef, useState } from 'react';

type User = {
  id: number;
  name: string;
  email: string;
  role: string;
  createdAt: string;
};

export default function UserManagement() {
  const actionRef = useRef<ActionType>();
  const [editingUser, setEditingUser] = useState<User | null>(null);

  const columns: ProColumns<User>[] = [
    { title: 'ID', dataIndex: 'id', width: 80 },
    { title: 'Name', dataIndex: 'name', copyable: true },
    { title: 'Email', dataIndex: 'email', copyable: true },
    {
      title: 'Role',
      dataIndex: 'role',
      valueType: 'select',
      valueEnum: {
        admin: { text: 'Admin', status: 'Success' },
        user: { text: 'User', status: 'Default' },
      },
    },
    {
      title: 'Created',
      dataIndex: 'createdAt',
      valueType: 'dateTime',
      search: false,
    },
    {
      title: 'Actions',
      valueType: 'option',
      render: (_, record) => [
        <a key="edit" onClick={() => setEditingUser(record)}>
          Edit
        </a>,
        <a key="delete" onClick={() => handleDelete(record.id)}>
          Delete
        </a>,
      ],
    },
  ];

  const handleDelete = async (id: number) => {
    message.success('Deleted successfully');
    actionRef.current?.reload();
  };

  return (
    <>
      <ProTable<User>
        columns={columns}
        actionRef={actionRef}
        request={async (params) => {
          return {
            data: [
              {
                id: 1,
                name: 'John Doe',
                email: '[email protected]',
                role: 'admin',
                createdAt: new Date().toISOString(),
              },
            ],
            success: true,
            total: 1,
          };
        }}
        rowKey="id"
        search={{
          labelWidth: 'auto',
        }}
        pagination={{
          defaultPageSize: 10,
        }}
        dateFormatter="string"
        headerTitle="Users"
        toolBarRender={() => [
          <ModalForm
            key="create"
            title="Create User"
            trigger={<Button type="primary">Create User</Button>}
            onFinish={async (values) => {
              message.success('User created');
              actionRef.current?.reload();
              return true;
            }}
          >
            <ProFormText
              name="name"
              label="Name"
              rules={[{ required: true }]}
            />
            <ProFormText
              name="email"
              label="Email"
              rules={[{ required: true, type: 'email' }]}
            />
          </ModalForm>,
        ]}
      />

      <ModalForm
        title="Edit User"
        open={!!editingUser}
        initialValues={editingUser || {}}
        onOpenChange={(visible) => !visible && setEditingUser(null)}
        onFinish={async (values) => {
          message.success('User updated');
          actionRef.current?.reload();
          setEditingUser(null);
          return true;
        }}
      >
        <ProFormText name="name" label="Name" rules={[{ required: true }]} />
        <ProFormText name="email" label="Email" rules={[{ required: true }]} />
      </ModalForm>
    </>
  );
}

Key concepts

ProComponents automatically renders fields based on valueType:
  • text - Plain text
  • date / dateTime - Date formatting
  • select - Dropdown with valueEnum
  • money - Currency formatting
  • percent - Percentage display
  • digit - Number formatting
  • progress - Progress bar
See all value types
The request prop handles async data fetching:
request={async (params, sort, filter) => {
  const data = await fetchData(params);
  
  return {
    data: data.list,
    success: true,
    total: data.total,
  };
}}
Use actionRef to programmatically control the table:
const actionRef = useRef<ActionType>();

actionRef.current?.reload();
actionRef.current?.reloadAndRest();
actionRef.current?.clearSelected();
ProTable automatically generates a search form from columns:
  • Add search: false to hide from search
  • Use hideInTable: true to show only in search
  • Customize with search.labelWidth, search.collapsed

Common patterns

const [selectedRow, setSelectedRow] = useState<User>();

<ProTable
  onRow={(record) => ({
    onClick: () => setSelectedRow(record),
  })}
/>

{selectedRow && (
  <ProDescriptions
    column={2}
    dataSource={selectedRow}
    columns={detailColumns}
  />
)}

Next steps

Explore Components

Learn about all available ProComponents

API Reference

Dive into detailed API documentation

Form Layouts

Explore different form layout options

Best Practices

Learn best practices and patterns

Build docs developers (and LLMs) love