탭
탭 (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;
커스텀 팝업 검색 (Custom Popup Search)
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 |
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 |
× | |
Position of tabs, please use tabPlacement instead |
top | right | bottom | left |
top |
× | ||
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 | - | |
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 |