탭

탭 (Tabs)

관련된 내용을 여러 개의 탭으로 나누어 한 번에 한 페이지만 보여주는 컴포넌트입니다.

출처: 문서

본문

언제 사용하나요 (When To Use)

Ant Design은 상황에 따라 3가지 유형의 Tabs를 제공합니다.

  • Card Tabs: 닫을 수 있는 뷰가 너무 많아 관리할 때
  • Normal Tabs: 페이지의 기능적 측면을 다룰 때
  • Radio.Button: 보조 탭으로 사용할 때

예제 (Examples)

기본 (Basic)

기본적으로 첫 번째 탭이 활성화됩니다.

import React from 'react';
import { Tabs } from 'antd';
import type { TabsProps } from 'antd';

const onChange = (key: string) => {
  console.log(key);
};

const items: TabsProps['items'] = [
  {
    key: '1',
    label: 'Tab 1',
    children: 'Content of Tab Pane 1',
  },
  {
    key: '2',
    label: 'Tab 2',
    children: 'Content of Tab Pane 2',
  },
  {
    key: '3',
    label: 'Tab 3',
    children: 'Content of Tab Pane 3',
  },
];

const App: React.FC = () => <Tabs defaultActiveKey="1" items={items} onChange={onChange} />;

export default App;

비활성화 (Disabled)

탭을 비활성화합니다.

import React from 'react';
import { Tabs } from 'antd';

const App: React.FC = () => (
  <Tabs
    defaultActiveKey="1"
    items={[
      {
        label: 'Tab 1',
        key: '1',
        children: 'Tab 1',
      },
      {
        label: 'Tab 2',
        key: '2',
        children: 'Tab 2',
        disabled: true,
      },
      {
        label: 'Tab 3',
        key: '3',
        children: 'Tab 3',
      },
    ]}
  />
);

export default App;

중앙 정렬 (Centered)

중앙 정렬된 탭입니다.

import React from 'react';
import { Tabs } from 'antd';

const App: React.FC = () => (
  <Tabs
    defaultActiveKey="1"
    centered
    items={Array.from({ length: 3 }).map((_, i) => {
      const id = String(i + 1);
      return {
        label: `Tab ${id}`,
        key: id,
        children: `Content of Tab Pane ${id}`,
      };
    })}
  />
);

export default App;

아이콘 (Icon)

아이콘이 있는 탭입니다. icon은 서드파티 아이콘 라이브러리의 단순한 <svg> 요소도 받아들이며, 이 경우 라벨과 세로 방향으로 중앙 정렬됩니다.

import React from 'react';
import { AndroidOutlined, AppleOutlined } from '@ant-design/icons';
import { Tabs } from 'antd';

// Icons from third-party libraries (e.g. lucide, react-icons) render as a bare `<svg>`
// rather than an `.anticon` wrapper. It stays vertically centred with the label.
const HeartIcon: React.FC = () => (
  <svg viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor" aria-hidden="true">
    <path d="M12 20.7l-1.4-1.3C5.4 14.8 2 11.7 2 8a5 5 0 0 1 10-1.7A5 5 0 0 1 22 8c0 3.7-3.4 6.8-8.6 11.4L12 20.7z" />
  </svg>
);

const App: React.FC = () => (
  <Tabs
    defaultActiveKey="2"
    items={[
      { key: '1', label: 'Tab 1', children: 'Tab 1', icon: <AppleOutlined /> },
      { key: '2', label: 'Tab 2', children: 'Tab 2', icon: <AndroidOutlined /> },
      { key: '3', label: 'Tab 3', children: 'Tab 3', icon: <HeartIcon /> },
    ]}
  />
);

export default App;

인디케이터 (Indicator)

indicator prop으로 커스텀 인디케이터 크기와 정렬을 설정합니다.

import React from 'react';
import { Segmented, Tabs } from 'antd';
import type { TabsProps } from 'antd';

const onChange = (key: string) => {
  console.log(key);
};

const items: TabsProps['items'] = [
  { key: '1', label: 'Tab 1', children: 'Content of Tab Pane 1' },
  { key: '2', label: 'Tab 2', children: 'Content of Tab Pane 2' },
  { key: '3', label: 'Tab 3', children: 'Content of Tab Pane 3' },
];

type Align = 'start' | 'center' | 'end';

const App: React.FC = () => {
  const [alignValue, setAlignValue] = React.useState<Align>('center');
  return (
    <>
      <Segmented
        value={alignValue}
        style={{ marginBottom: 8 }}
        onChange={setAlignValue}
        options={['start', 'center', 'end']}
      />
      <Tabs
        defaultActiveKey="1"
        items={items}
        onChange={onChange}
        indicator={{ size: (origin) => origin - 20, align: alignValue }}
      />
    </>
  );
};

export default App;

슬라이드 (Slide)

더 많은 탭을 수용하기 위해 탭들이 좌우(또는 상하)로 슬라이드할 수 있습니다.

import React, { useState } from 'react';
import type { RadioChangeEvent, TabsProps } from 'antd';
import { Radio, Tabs } from 'antd';

const App: React.FC = () => {
  const [mode, setMode] = useState<TabsProps['tabPlacement']>('top');

  const handleModeChange = (e: RadioChangeEvent) => {
    setMode(e.target.value);
  };

  return (
    <div>
      <Radio.Group onChange={handleModeChange} value={mode} style={{ marginBottom: 8 }}>
        <Radio.Button value="top">Horizontal</Radio.Button>
        <Radio.Button value="left">Vertical</Radio.Button>
      </Radio.Group>
      <Tabs
        defaultActiveKey="1"
        tabPlacement={mode}
        style={{ height: 220 }}
        items={Array.from({ length: 30 }, (_, i) => {
          const id = String(i);
          return {
            label: `Tab-${id}`,
            key: id,
            disabled: i === 28,
            children: `Content of tab ${id}`,
          };
        })}
      />
    </div>
  );
};

export default App;

추가 콘텐츠 (Extra content)

Tabs의 오른쪽, 왼쪽 또는 양쪽 모두에 추가 동작을 넣을 수 있습니다.

import React, { useMemo, useState } from 'react';
import { Button, Checkbox, Divider, Tabs } from 'antd';

const CheckboxGroup = Checkbox.Group;

const operations = <Button>Extra Action</Button>;

const operationsSlot: Record<PositionType, React.ReactNode> = {
  left: <Button style={{ marginInlineEnd: 16 }}>Left Extra Action</Button>,
  right: <Button style={{ marginInlineStart: 16 }}>Right Extra Action</Button>,
};

const options = ['left', 'right'];

type PositionType = 'left' | 'right';

type TabBarExtraMap = Partial<Record<PositionType, React.ReactNode>>;

const items = Array.from({ length: 3 }).map((_, i) => {
  const id = String(i + 1);
  return {
    label: `Tab ${id}`,
    key: id,
    children: `Content of tab ${id}`,
  };
});

const App: React.FC = () => {
  const [position, setPosition] = useState<PositionType[]>(['left', 'right']);

  const slot = useMemo(() => {
    if (position.length === 0) {
      return null;
    }
    return position.reduce<TabBarExtraMap>(
      (acc, direction) => ({ ...acc, [direction]: operationsSlot[direction] }),
      {},
    );
  }, [position]);

  return (
    <>
      <Tabs tabBarExtraContent={operations} items={items} />
      <br />
      <br />
      <br />
      <div>You can also specify its direction or both side</div>
      <Divider />
      <CheckboxGroup<PositionType> options={options} value={position} onChange={setPosition} />
      <br />
      <br />
      <Tabs tabBarExtraContent={slot} items={items} />
    </>
  );
};

export default App;

크기 (Size)

큰 크기의 탭은 보통 페이지 헤더에 사용되고, 작은 크기는 Modal에서 사용할 수 있습니다.

import React, { useRef, useState } from 'react';
import type { RadioChangeEvent, TabsProps } from 'antd';
import { Radio, Tabs } from 'antd';

type TargetKey = React.MouseEvent | React.KeyboardEvent | string;

const App: React.FC = () => {
  const [size, setSize] = useState<'small' | 'medium' | 'large'>('small');
  const [activeKey, setActiveKey] = useState('1');
  const [items, setItems] = useState<TabsProps['items']>([
    {
      label: 'Tab 1',
      key: '1',
      children: 'Content of editable tab 1',
    },
    {
      label: 'Tab 2',
      key: '2',
      children: 'Content of editable tab 2',
    },
    {
      label: 'Tab 3',
      key: '3',
      children: 'Content of editable tab 3',
    },
  ]);
  const newTabIndex = useRef(0);

  const add = () => {
    const newActiveKey = `newTab${newTabIndex.current++}`;
    setItems([
      ...(items || []),
      {
        label: 'New Tab',
        key: newActiveKey,
        children: 'Content of new Tab',
      },
    ]);
    setActiveKey(newActiveKey);
  };

  const remove = (targetKey: TargetKey) => {
    if (!items) {
      return;
    }
    const targetIndex = items.findIndex((item) => item.key === targetKey);
    const newItems = items.filter((item) => item.key !== targetKey);

    if (newItems.length && targetKey === activeKey) {
      const newActiveKey =
        newItems[targetIndex === newItems.length ? targetIndex - 1 : targetIndex].key;
      setActiveKey(newActiveKey);
    }

    setItems(newItems);
  };

  const onEdit = (targetKey: TargetKey, action: 'add' | 'remove') => {
    if (action === 'add') {
      add();
    } else {
      remove(targetKey);
    }
  };

  const onChange = (e: RadioChangeEvent) => {
    setSize(e.target.value);
  };

  return (
    <div>
      <Radio.Group value={size} onChange={onChange} style={{ marginBottom: 16 }}>
        <Radio.Button value="small">Small</Radio.Button>
        <Radio.Button value="medium">Medium</Radio.Button>
        <Radio.Button value="large">Large</Radio.Button>
      </Radio.Group>
      <Tabs
        defaultActiveKey="1"
        size={size}
        style={{ marginBottom: 32 }}
        items={Array.from({ length: 3 }).map((_, i) => {
          const id = String(i + 1);
          return {
            label: `Tab ${id}`,
            key: id,
            children: `Content of tab ${id}`,
          };
        })}
      />
      <Tabs
        defaultActiveKey="1"
        type="card"
        size={size}
        style={{ marginBottom: 32 }}
        items={Array.from({ length: 3 }).map((_, i) => {
          const id = String(i + 1);
          return {
            label: `Card Tab ${id}`,
            key: id,
            children: `Content of card tab ${id}`,
          };
        })}
      />
      <Tabs
        type="editable-card"
        size={size}
        activeKey={activeKey}
        onChange={setActiveKey}
        onEdit={onEdit}
        items={items}
      />
    </div>
  );
};

export default App;

배치 (Placement)

탭의 배치는 start, end, top 또는 bottom입니다. 모바일에서는 자동으로 top으로 전환됩니다.

import React, { useState } from 'react';
import type { RadioChangeEvent, TabsProps } from 'antd';
import { Radio, Space, Tabs } from 'antd';

const App: React.FC = () => {
  const [tabPlacement, setTabPlacement] = useState<TabsProps['tabPlacement']>('start');

  const changeTabPlacement = (e: RadioChangeEvent) => {
    setTabPlacement(e.target.value);
  };

  return (
    <>
      <Space style={{ marginBottom: 24 }}>
        Tab placement:
        <Radio.Group value={tabPlacement} onChange={changeTabPlacement}>
          <Radio.Button value="top">top</Radio.Button>
          <Radio.Button value="bottom">bottom</Radio.Button>
          <Radio.Button value="start">start</Radio.Button>
          <Radio.Button value="end">end</Radio.Button>
        </Radio.Group>
      </Space>
      <Tabs
        tabPlacement={tabPlacement}
        items={Array.from({ length: 3 }).map((_, i) => {
          const id = String(i + 1);
          return {
            label: `Tab ${id}`,
            key: id,
            children: `Content of Tab ${id}`,
          };
        })}
      />
    </>
  );
};

export default App;

popupRender prop으로 Tabs의 more 드롭다운 메뉴를 커스터마이즈하며, 검색과 키보드 내비게이션을 지원합니다.

import React, { useMemo, useRef, useState } from 'react';
import { Input, Menu, Tabs } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import { createStyles } from 'antd-style';
import type { TabsProps } from 'antd';

const useSearchPopupStyle = createStyles(({ token, css }) => ({
  container: css`
    width: 200px;
    background: ${token.colorBgContainer};
    box-shadow: ${token.boxShadow};
    border-radius: ${token.borderRadiusLG}px;
    overflow: hidden;
  `,
  searchWrapper: css`
    padding: ${token.paddingXS}px ${token.paddingSM}px;
    border-bottom: ${token.lineWidth}px ${token.lineType} ${token.colorBorder};
  `,
  menuWrapper: css`
    max-height: 300px;
    overflow-y: auto;
  `,
  empty: css`
    padding: ${token.paddingSM}px;
    color: ${token.colorTextDisabled};
    text-align: center;
  `,
}));

const items: TabsProps['items'] = Array.from({ length: 30 }, (_, i) => {
  const id = String(i);
  return {
    label: `Tab-${id}`,
    key: id,
    disabled: i === 28,
    children: `Content of tab ${id}`,
  };
});

const SearchPopup = ({
  restTabs,
  activeKey,
  onChange,
}: {
  restTabs: any[];
  activeKey: string;
  onChange: (key: string) => void;
}) => {
  const [searchTerm, setSearchTerm] = useState('');
  const menuRef = useRef<any>(null);
  const { styles } = useSearchPopupStyle();

  const fullTabs = useMemo(
    () => restTabs.map((item) => ({ key: item.key, label: item.label, disabled: item.disabled })),
    [restTabs],
  );

  const filterTabs = useMemo(
    () =>
      !searchTerm
        ? fullTabs
        : fullTabs.filter((tab) =>
            String(tab.label).toLowerCase().includes(searchTerm.toLowerCase()),
          ),
    [fullTabs, searchTerm],
  );

  return (
    <div className={styles.container}>
      <div className={styles.searchWrapper}>
        <Input
          placeholder="Search tabs..."
          prefix={<SearchOutlined />}
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
              menuRef.current?.focus();
            }
          }}
          allowClear
        />
      </div>
      <div className={styles.menuWrapper}>
        <Menu
          ref={menuRef}
          defaultSelectedKeys={[activeKey]}
          items={filterTabs}
          onClick={({ key }) => {
            setSearchTerm('');
            onChange(key);
          }}
        />
      </div>
      {filterTabs.length === 0 && <div className={styles.empty}>No matching tabs</div>}
    </div>
  );
};

const App: React.FC = () => {
  const [activeKey, setActiveKey] = React.useState('1');

  return (
    <Tabs
      activeKey={activeKey}
      onChange={setActiveKey}
      items={items}
      more={{
        trigger: 'click',
        placement: 'bottomLeft',
        popupRender: (_, { restTabs, onClose }) => (
          <SearchPopup
            restTabs={restTabs}
            activeKey={activeKey}
            onChange={(key) => {
              setActiveKey(key);
              onClose();
            }}
          />
        ),
      }}
    />
  );
};

export default App;

카드 타입 탭 (Card type tab)

세로 모드를 지원하지 않는 또 다른 유형의 Tabs입니다.

import React from 'react';
import { Tabs } from 'antd';

const onChange = (key: string) => {
  console.log(key);
};

const App: React.FC = () => (
  <Tabs
    onChange={onChange}
    type="card"
    items={Array.from({ length: 3 }).map((_, i) => {
      const id = String(i + 1);
      return {
        label: `Tab ${id}`,
        key: id,
        children: `Content of Tab Pane ${id}`,
      };
    })}
  />
);

export default App;

탭 추가 & 닫기 (Add & close tab)

카드 타입 Tabs만 추가 및 닫기를 지원합니다. closable={false}를 사용해 닫기를 비활성화할 수 있습니다.

import React, { useRef, useState } from 'react';
import { Tabs } from 'antd';

type TargetKey = React.MouseEvent | React.KeyboardEvent | string;

const initialItems = [
  { label: 'Tab 1', children: 'Content of Tab 1', key: '1' },
  { label: 'Tab 2', children: 'Content of Tab 2', key: '2' },
  {
    label: 'Tab 3',
    children: 'Content of Tab 3',
    key: '3',
    closable: false,
  },
];

const App: React.FC = () => {
  const [activeKey, setActiveKey] = useState(initialItems[0].key);
  const [items, setItems] = useState(initialItems);
  const newTabIndex = useRef(0);

  const onChange = (newActiveKey: string) => {
    setActiveKey(newActiveKey);
  };

  const add = () => {
    const newActiveKey = `newTab${newTabIndex.current++}`;
    const newPanes = [...items];
    newPanes.push({ label: 'New Tab', children: 'Content of new Tab', key: newActiveKey });
    setItems(newPanes);
    setActiveKey(newActiveKey);
  };

  const remove = (targetKey: TargetKey) => {
    let newActiveKey = activeKey;
    let lastIndex = -1;
    items.forEach((item, i) => {
      if (item.key === targetKey) {
        lastIndex = i - 1;
      }
    });
    const newPanes = items.filter((item) => item.key !== targetKey);
    if (newPanes.length && newActiveKey === targetKey) {
      if (lastIndex >= 0) {
        newActiveKey = newPanes[lastIndex].key;
      } else {
        newActiveKey = newPanes[0].key;
      }
    }
    setItems(newPanes);
    setActiveKey(newActiveKey);
  };

  const onEdit = (
    targetKey: React.MouseEvent | React.KeyboardEvent | string,
    action: 'add' | 'remove',
  ) => {
    if (action === 'add') {
      add();
    } else {
      remove(targetKey);
    }
  };

  return (
    <Tabs
      type="editable-card"
      onChange={onChange}
      activeKey={activeKey}
      onEdit={onEdit}
      items={items}
    />
  );
};

export default App;

새 탭 트리거 커스터마이즈 (Customized trigger of new tab)

기본 더하기 아이콘을 숨기고, 커스텀 트리거에 이벤트를 바인딩합니다.

import React, { useRef, useState } from 'react';
import { Button, Tabs } from 'antd';

type TargetKey = React.MouseEvent | React.KeyboardEvent | string;

const defaultPanes = Array.from({ length: 2 }).map((_, index) => {
  const id = String(index + 1);
  return { label: `Tab ${id}`, children: `Content of Tab Pane ${index + 1}`, key: id };
});

const App: React.FC = () => {
  const [activeKey, setActiveKey] = useState(defaultPanes[0].key);
  const [items, setItems] = useState(defaultPanes);
  const newTabIndex = useRef(0);

  const onChange = (key: string) => {
    setActiveKey(key);
  };

  const add = () => {
    const newActiveKey = `newTab${newTabIndex.current++}`;
    setItems([...items, { label: 'New Tab', children: 'New Tab Pane', key: newActiveKey }]);
    setActiveKey(newActiveKey);
  };

  const remove = (targetKey: TargetKey) => {
    const targetIndex = items.findIndex((pane) => pane.key === targetKey);
    const newPanes = items.filter((pane) => pane.key !== targetKey);
    if (newPanes.length && targetKey === activeKey) {
      const { key } = newPanes[targetIndex === newPanes.length ? targetIndex - 1 : targetIndex];
      setActiveKey(key);
    }
    setItems(newPanes);
  };

  const onEdit = (targetKey: TargetKey, action: 'add' | 'remove') => {
    if (action === 'add') {
      add();
    } else {
      remove(targetKey);
    }
  };

  return (
    <div>
      <div style={{ marginBottom: 16 }}>
        <Button onClick={add}>ADD</Button>
      </div>
      <Tabs
        hideAdd
        onChange={onChange}
        activeKey={activeKey}
        type="editable-card"
        onEdit={onEdit}
        items={items}
      />
    </div>
  );
};

export default App;

탭 바 커스터마이즈 (Customized bar of tab)

react-sticky-box와 renderTabBar를 사용합니다.

import React from 'react';
import type { TabsProps } from 'antd';
import { Tabs, theme } from 'antd';
import StickyBox from 'react-sticky-box';

const items = Array.from({ length: 3 }).map((_, i) => {
  const id = String(i + 1);
  return {
    label: `Tab ${id}`,
    key: id,
    children: `Content of Tab Pane ${id}`,
    style: i === 0 ? { height: 200 } : undefined,
  };
});

const App: React.FC = () => {
  const {
    token: { colorBgContainer },
  } = theme.useToken();
  const renderTabBar: TabsProps['renderTabBar'] = (props, DefaultTabBar) => (
    <StickyBox offsetTop={64} offsetBottom={20} style={{ zIndex: 1 }}>
      <DefaultTabBar {...props} style={{ background: colorBgContainer }} />
    </StickyBox>
  );
  return <Tabs defaultActiveKey="1" renderTabBar={renderTabBar} items={items} />;
};

export default App;

드래그 가능한 Tabs (Draggable Tabs)

dnd-kit을 사용해 탭들을 드래그 가능하게 만듭니다.

import React, { useState } from 'react';
import type { DragEndEvent } from '@dnd-kit/core';
import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
import {
  arrayMove,
  horizontalListSortingStrategy,
  SortableContext,
  useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Tabs } from 'antd';
import type { TabsProps } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles((props) => {
  const { css, prefixCls } = props;
  return {
    root: css`
      margin: 0;
      &.${prefixCls}-tabs-editable {
        .${prefixCls}-tabs-nav {
          .${prefixCls}-tabs-tab {
            /* set transition to none when type="editable-card" */
            transition: none;
          }
        }
      }
    `,
  };
});

interface DraggableTabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
  'data-node-key': string;
}

const DraggableTabNode: React.FC<Readonly<DraggableTabPaneProps>> = (props) => {
  const { className, ...rest } = props;

  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
    id: rest['data-node-key'],
  });

  const style: React.CSSProperties = {
    ...rest.style,
    transform: CSS.Translate.toString(transform),
    transition,
    cursor: 'move',
  };

  return React.cloneElement(rest.children as React.ReactElement<any>, {
    ref: setNodeRef,
    style,
    ...attributes,
    ...listeners,
  });
};

const App: React.FC = () => {
  const { styles } = useStyles();

  const [items, setItems] = useState<NonNullable<TabsProps['items']>>([
    { key: '1', label: 'Tab 1', children: 'Content of Tab Pane 1' },
    { key: '2', label: 'Tab 2', children: 'Content of Tab Pane 2' },
    { key: '3', label: 'Tab 3', children: 'Content of Tab Pane 3' },
  ]);

  const sensor = useSensor(PointerSensor, { activationConstraint: { distance: 10 } });

  const onDragEnd = ({ active, over }: DragEndEvent) => {
    if (active.id !== over?.id) {
      setItems((prev) => {
        const activeIndex = prev.findIndex((i) => i.key === active.id);
        const overIndex = prev.findIndex((i) => i.key === over?.id);
        return arrayMove(prev, activeIndex, overIndex);
      });
    }
  };

  return (
    <Tabs
      items={items}
      rootClassName={styles.root}
      renderTabBar={(tabBarProps, DefaultTabBar) => (
        <DndContext sensors={[sensor]} onDragEnd={onDragEnd} collisionDetection={closestCenter}>
          <SortableContext items={items.map((i) => i.key)} strategy={horizontalListSortingStrategy}>
            <DefaultTabBar {...tabBarProps}>
              {(node) => (
                <DraggableTabNode
                  {...(node as React.ReactElement<DraggableTabPaneProps>).props}
                  key={node.key}
                >
                  {node}
                </DraggableTabNode>
              )}
            </DefaultTabBar>
          </SortableContext>
        </DndContext>
      )}
    />
  );
};

export default App;

커스텀 시맨틱 DOM 스타일링 (Custom semantic dom styling)

classNames와 styles에 객체 또는 함수를 넘겨서 Tabs의 시맨틱 DOM 스타일을 커스터마이즈할 수 있습니다.

import React from 'react';
import { Flex, Tabs } from 'antd';
import type { GetProp, TabsProps } from 'antd';
import { createStaticStyles } from 'antd-style';

const classNames = createStaticStyles(({ css }) => ({
  root: css`
    border-width: 2px;
    border-style: dashed;
    padding: 16px;
    margin-bottom: 10px;
  `,
}));

const stylesObject: TabsProps['styles'] = {
  root: { borderWidth: 2, borderStyle: 'dashed', padding: 16, marginBottom: 10 },
  header: { backgroundColor: 'rgba(245,245,245,0.5)' },
  item: { fontWeight: 'bold', color: '#1890ff', padding: `6px 10px` },
  indicator: { backgroundColor: 'rgba(255,77,79, 0.3)', height: 4 },
  body: { backgroundColor: 'rgba(230,247,255,0.8)' },
  content: { padding: 16 },
};

const stylesFn: TabsProps['styles'] = (info): GetProp<TabsProps, 'styles', 'Return'> => {
  if (info.props.type === 'card') {
    return {
      root: { backgroundColor: 'rgba(250,250,250, 0.8)', borderColor: '#d9d9d9' },
      header: { textAlign: 'start' },
    };
  }
  return {};
};

const items = [
  {
    key: '1',
    label: 'Tab 1',
    children: 'Content of Tab Pane 1',
  },
  {
    key: '2',
    label: 'Tab 2',
    children: 'Content of Tab Pane 2',
  },
  {
    key: '3',
    label: 'Tab 3',
    children: 'Content of Tab Pane 3',
  },
];

const App: React.FC = () => {
  const shareProps: TabsProps = {
    items,
    defaultActiveKey: '1',
    classNames,
  };

  return (
    <Flex vertical gap="medium">
      <Tabs {...shareProps} styles={stylesObject} />
      <Tabs tabPlacement="start" type="card" {...shareProps} styles={stylesFn} />
    </Flex>
  );
};

export default App;

API

Common props ref:Common props

Tabs

Property Description Type Default Version Global Config
activeKey Current TabPane's key string - ×
addIcon Customize add icon, only works with type="editable-card" ReactNode <PlusOutlined /> 4.4.0 5.14.0
animated Whether to change tabs with animation. boolean | { inkBar: boolean, tabPane: boolean } { inkBar: true, tabPane: false } ×
centered Centers tabs boolean false 4.4.0 ×
classNames Customize class for each semantic structure inside the component. Supports object or function. Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> - 6.0.0
defaultActiveKey Initial active TabPane's key, if activeKey is not set string The key of first tab ×
hideAdd Hide plus icon or not. Only works while type="editable-card" boolean false ×
indicator Customize size and align of indicator { size?: number | (origin: number) => number; align: start | center | end; } - 5.13.0 5.13.0
items Configure tab content TabItemType [] 4.23.0 ×
more Customize the collapse menu MoreProps { icon: <EllipsisOutlined /> , trigger: 'hover' } more.icon: 5.17.0
removeIcon The custom icon of remove, only works with type="editable-card" ReactNode <CloseOutlined /> 5.15.0 5.15.0
popupClassName className for more dropdown, please use classNames.popup instead string - 4.21.0 ×
renderTabBar Replace the TabBar (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement - ×
size Preset tab bar size large | medium | small medium ×
styles Customize inline style for each semantic structure inside the component. Supports object or function. Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 6.0.0
tabBarExtraContent Extra content in tab bar ReactNode | {left?: ReactNode, right?: ReactNode} - object: 4.6.0 ×
tabBarGutter The gap between tabs number - ×
tabBarStyle Tab bar style object CSSProperties - ×
tabPlacement Placement of tabs top | end | bottom | start top ×
tabPosition Position of tabs, please use tabPlacement instead top | right | bottom | left top ×
destroyInactiveTabPane Whether destroy inactive TabPane when change tab, use destroyOnHidden instead boolean false ×
destroyOnHidden Whether destroy inactive TabPane when change tab boolean false 5.25.0 ×
type Basic style of tabs line | card | editable-card line ×
onChange Callback executed when active tab is changed (activeKey: string) => void - ×
onEdit Callback executed when tab is added or removed. Only works while type="editable-card" (action === 'add' ? event : targetKey, action) => void - ×
onTabClick Callback executed when tab is clicked (key: string, event: MouseEvent) => void - ×
onTabScroll Trigger when tab scroll ({ direction: left | right | top | bottom }) => void - 4.3.0 ×

더 많은 옵션은 @rc-component/tabs에서 확인하세요.

TabItemType

Property Description Type Default Version
closeIcon Customize close icon in TabPane's head. Only works while type="editable-card". 5.7.0: close button will be hidden when setting to null or false ReactNode -
destroyInactiveTabPane Whether destroy inactive TabPane when change tab, use destroyOnHidden instead boolean false 5.11.0
destroyOnHidden Whether destroy inactive TabPane when change tab boolean false 5.25.0
disabled Set TabPane disabled boolean false
forceRender Forced render of content in tabs, not lazy render after clicking on tabs boolean false
key TabPane's key string -
label Tab header text element ReactNode -
icon Tab header icon element ReactNode - 5.12.0
children Tab content element ReactNode -
closable Whether a close (x) button is visible, Only works while type="editable-card" boolean true

MoreProps

Property Description Type Default Version
icon The custom icon ReactNode -
popupRender Customize the dropdown menu render (menu: ReactElement, info: { restTabs: Tab[], onClose: () => void }) => ReactElement - 6.6.0
DropdownProps Support other Dropdown props

시맨틱 DOM (Semantic DOM)

https://ant.design/components/tabs/semantic.md

디자인 토큰 (Design Token)

컴포넌트 토큰 (Component Token - Tabs)

Token Name Description Type Default Value
cardBg Background color of card tab string rgba(0,0,0,0.02)
cardGutter Gutter of card tab number 2
cardHeight Height of card tab number 40
cardHeightLG Height of large card tab number 48
cardHeightSM Height of small card tab number 32
cardPadding Padding of card tab string 8px 16px
cardPaddingLG Padding of large card tab string 11px 16px
cardPaddingSM Padding of small card tab string 4px 8px
horizontalItemGutter Horizontal gutter of horizontal tab number 32
horizontalItemMargin Horizontal margin of horizontal tab item string
horizontalItemMarginRTL Horizontal margin of horizontal tab item (RTL) string
horizontalItemPadding Horizontal padding of horizontal tab item string 12px 0
horizontalItemPaddingLG Horizontal padding of large horizontal tab item string 16px 0
horizontalItemPaddingSM Horizontal padding of small horizontal tab item string 8px 0
horizontalMargin Horizontal margin of horizontal tab string 0 0 16px 0
inkBarColor Color of indicator string #1677ff
itemActiveColor Text color of active tab string #0958d9
itemColor Text color of tab string rgba(0,0,0,0.88)
itemHoverColor Text color of hover tab string #4096ff
itemSelectedColor Text color of selected tab string #1677ff
titleFontSize Font size of title number 14
titleFontSizeLG Font size of large title number 16
titleFontSizeSM Font size of small title number 14
verticalItemMargin Vertical margin of vertical tab item string 16px 0 0 0
verticalItemPadding Vertical padding of vertical tab item string 8px 24px
zIndexPopup z-index of dropdown menu number 1050

글로벌 토큰 (Global Token)

Token Name Description Type Default Value
borderRadius Border radius of base components number
borderRadiusLG LG size border radius, used in some large border radius components, such as Card, Modal and other components. number
boxShadowSecondary Control the secondary box shadow style of an element. string
colorBgContainer Container background color, e.g: default button, input box, etc. Be sure not to confuse this with colorBgElevated. string
colorBorder Default border color, used to separate different elements, such as: form separator, card separator, etc. string
colorBorderSecondary Slightly lighter than the default border color, this color is the same as colorSplit. Solid color is used. string
colorIcon Weak action. Such as allowClear or Alert close button string
colorPrimaryBorder The stroke color under the main color gradient, used on the stroke of components such as Slider. string
colorText Default text color which comply with W3C standards, and this color is also the darkest neutral color. string
colorTextDisabled Control the color of text in disabled state. string
colorTextHeading Control the font color of heading. string
controlHeight The height of the basic controls such as buttons and input boxes in Ant Design number
controlHeightLG LG component height number
controlItemBgHover Control the background color of control component item when hovering. string
fontFamily The font family of Ant Design prioritizes the default interface font of the system, and provides a set of alternative font libraries that are suitable for screen display to maintain the readability and readability of the font under different platforms and browsers, reflecting the friendly, stable and professional characteristics. string
fontSize The most widely used font size in the design system, from which the text gradient will be derived. number
fontSizeSM Small font size number
lineHeight Line height of text. number
lineHeightLG Line height of large text. number
lineType Border style of base components string
lineWidth Border width of base components number
lineWidthBold The default line width of the outline class components, such as Button, Input, Select, etc. number
lineWidthFocus Control the width of the line when the component is in focus state. number
margin Control the margin of an element, with a medium size. number
marginSM Control the margin of an element, with a medium-small size. number
marginXS Control the margin of an element, with a small size. number
marginXXS Control the margin of an element, with the smallest size. number
motionDurationMid Motion speed, medium speed. Used for medium element animation interaction. string
motionDurationSlow Motion speed, slow speed. Used for large element animation interaction. string
motionEaseInOut Preset motion curve. string
motionEaseInQuint Preset motion curve. string
motionEaseOutQuint Preset motion curve. string
paddingLG Control the large padding of the element. number
paddingSM Control the small padding of the element. number
paddingXXS Control the extra extra small padding of the element. number

더 알아보기 (Learn more)