Switches between same-level content groups, typically at the top of forms or standard list interfaces.
-
Currently, only one Tabs component can be used on a single page.
-
In base libraries 2.x, an embedded scroll-view may become unscrollable. To fix this, prevent touch events from bubbling up from the scroll-view using `catchTouchStart` and `catchTouchMove`.
-
Tabs use the transform style internally for the carousel effect. This can cause display issues with nested pop-up layers. Avoid nesting components that contain pop-up layers. Alternatively, you can use the `fallback` property to implement a simple version of the carousel yourself. For more information, see the fallback demo below.
Properties
Tabs
|
Property |
Type |
Required |
Default |
Description |
|
adjustHeight |
string |
No |
'current' |
Automatically sets the container height to the height of the specified panel. |
|
activeClass |
string |
No |
'' |
The class of the swiper-item when it is visible. |
|
animation |
boolean |
No |
false |
Whether to use a transition animation. |
|
className |
string |
No |
- |
The class name. |
|
easingFunction |
string |
No |
'default' |
The easing animation type for tab switching. |
|
index |
number |
No |
0 |
The active index. |
|
nextMargin |
string |
No |
'0px' |
The next margin. The unit is px. In version 1.9.0, only the horizontal direction is supported. |
|
plus |
string | slot |
No |
- |
Custom action button in the upper-right corner. |
|
previousMargin |
string |
No |
'0px' |
The previous margin. The unit is px. In version 1.9.0, only the horizontal direction is supported. |
|
snapToEdge |
boolean |
No |
false |
When there are two or more swiper-items, `circular` is off, and `previous-margin` or `next-margin` is on, this specifies if the margin applies to the first and last items. |
|
sticky |
boolean |
No |
false |
Whether to enable the sticky header effect. |
|
swipeRatio |
number |
No |
0.2 |
The threshold for triggering a tab switch with a horizontal swipe gesture. The swiper-item switches when the swipe distance exceeds this threshold. |
|
swipeSpeed |
number |
No |
0.05 |
Controls the swipe distance of the swiper-item relative to the user's gesture. A smaller value means the swiper-item moves less for the same gesture distance. |
|
swipeable |
boolean |
No |
false |
Whether to allow switching tabs with swipe gestures. |
|
title |
slot-scope |
No |
- |
Customizes the tab title style. This is available only when `type` is set to `basis`. |
|
touchAngle |
number |
No |
45 |
The swipe angle at which a horizontal swipe gesture takes effect. The angle is calculated based on the coordinates of the `touchstart` event and the first `touchmove` event. A smaller value requires a more precise horizontal swipe direction from the user. |
|
type |
'basis' | 'capsule' | 'mixin' |
No |
'basis' |
The type.
|
TabItem
|
Property |
Type |
Required |
Default |
Description |
|
tab |
{title: string; subTitle?: string; badge?: number; disabled?: boolean}[] |
Yes |
- |
The content of each tab. |
|
className |
string |
No |
- |
The class name. |
Events
|
Event name |
Description |
Type |
|
onChange |
Triggered when the active panel changes. |
(index: number) => void |
|
onAnimationEnd |
The `onAnimationEnd` event of the internal swiper component. Available in base library 1.50.0 and later. |
(e: any) => void |
|
onTouchStart |
The `onTouchStart` event of the internal swiper component. Available in base library 2.x and later. |
(e: any) => void |
|
onTransition |
The `onTransition` event of the internal swiper component. Available in base library 2.x and later. |
(e: any) => void |
Slots
Tabs
|
Name |
Description |
|
plus |
Extra content for the form item. |
Scoped slots
Tabs
|
Name |
Description |
|
title |
Customizes the tab title style. |
Style classes
Tabs
|
Class name |
Description |
|
amd-tabs |
The overall style. |
|
amd-tabs-bar |
The style of the title area at the top. |
|
|
The style of the title area when it is active. |
|
|
The style of the indicator when the title area is active. |
|
amd-tabs-bar |
The style of the title area at the top. |
|
amd-tabs-bar-sticky |
The style for the sticky state. |
|
amd-tabs-bar-plus |
Tag style in the top-right corner. |
|
amd-tabs-bar-fade |
The style for the fade effect on both sides. |
|
amd-tabs-bar-fade-left |
The style for the fade effect on the left. |
|
amd-tabs-bar-fade-right |
The style for the fade effect on the right. |
|
amd-tabs-bar-scroll-view |
The style of the internal ScrollView component. |
|
amd-tabs-bar-wrap |
The style of each title. |
|
amd-tabs-bar-item |
The style of each title. |
|
amd-tabs-bar-basis |
The style of each title when `type` is `basis`. |
|
amd-tabs-bar-capsule |
The style of each title when `type` is `capsule`. |
|
amd-tabs-bar-capsule-title |
The style of the text inside each title when `type` is `capsule`. |
|
amd-tabs-bar-capsule-badge |
The style of the badge inside each title when `type` is `capsule`. |
|
amd-tabs-bar-mixin |
The style of each title when `type` is `mixin`. |
|
amd-tabs-bar-mixin-title |
The style of the text in each title when `type` is `mixin`. |
|
amd-tabs-bar-mixin-subtitle |
The style of the subtitle in each title when `type` is `mixin`. |
|
amd-tabs-bar-disabled |
The style of a disabled tab. |
TabItem
|
Class name |
Description |
|
amd-tabs-item |
The style of the tab menu. |
|
amd-tabs-item-pane |
The style of the content panel below the tabs. |
Code examples
Basic usage
The following code is an example of `index.axml`:
<view>
<tabs
index="{{index}}"
type="{{type}}"
animation="{{animation}}"
swipeable="{{swipeable}}"
sticky="{{sticky}}"
onChange="handleChangeTab">
<view a:if="{{plusSlot}}" slot="plus" >
<icon
size="small"
type="AddOutline"
className="plus-icon"
onTap="handleClickIcon" />
</view>
<view
a:if="{{titleSlot}}"
slot-scope="prop"
slot="title">
{{prop.tab.title + ' slot'}}
</view>
<block a:for="{{tabs}}">
<tab-content
a:if="{{index === 1}}"
tab="{{item}}">
<view>
{{item.title}}
<view
a:for="{{height}}">
........Height adapts to content........
</view>
</view>
</tab-content>
<tab-content
a:elif="{{index === 3}}"
tab="{{item}}">
<view style="height: 30vh">
........{{item.title}}........
<text>Content with parent-defined height</text>
</view>
</tab-content>
<tab-content tab="{{item}}" a:else badge="{{item.badge}}">
<view style="height: 20vh">
........{{item.title}}........
</view>
</tab-content>
</block>
</tabs>
<list radius className="actions">
<list-item>
Number of tabs
<radio-group
slot="extra"
class="radio-group"
name="tabsNumber"
onChange="handleChangeTabNum" >
<label
class="radio"
a:for="{{tabsNumber}}">
<radio
value="{{item.name}}"
checked="{{item.checked}}" />
<text
class="radio-text">
{{item.value}}
</text>
</label>
</radio-group>
</list-item>
<list-item>
type
<radio-group
slot="extra"
class="radio-group"
name="tabsType"
onChange="handleChangeType" >
<label
class="radio"
a:for="{{tabsType}}">
<radio
value="{{item.name}}"
checked="{{item.checked}}" />
<text
class="radio-text">
{{item.value}}
</text>
</label>
</radio-group>
</list-item>
<list-item>
Content transition animation<switch checked="{{animation}}" controlled onChange="handleChangeAnimation" slot="extra"/>
</list-item>
<list-item>
Support swipe to switch<switch checked="{{swipeable}}" controlled onChange="handleChangeSwipeable" slot="extra"/>
</list-item>
<list-item brief="sticky effect">
Sticky header<switch checked="{{sticky}}" controlled onChange="handleChangeSticky" slot="extra"/>
</list-item>
<list-item brief="Custom tab display, has higher priority than configuration">
title slot<switch checked="{{titleSlot}}" controlled onChange="handleChangeTitleSlot" slot="extra"/>
</list-item>
<list-item brief="Action button in the upper-right corner">
plus slot<switch checked="{{plusSlot}}" controlled onChange="handleChangePlusSlot" slot="extra"/>
</list-item>
</list>
</view>
The following code is an example of `index.js`:
Page({
data: {
index: 0,
height: 30,
type: 'basis',
animation: true,
swipeable: true,
titleSlot: false,
plusSlot: true,
sticky: false,
tabs: [
{
title: 'Tab 1',
subTitle: 'Description',
},
{
title: 'Tab 2',
subTitle: 'Description text',
},
{
title: 'Tab 3',
subTitle: 'Description',
},
],
tabsType: [
{ name: 'basis', value: 'Basic', checked: true },
{ name: 'capsule', value: 'Capsule' },
{ name: 'mixin', value: 'With description' },
],
tabsNumber: [
{ name: '1', value: 'One' },
{ name: '2', value: 'Two' },
{ name: '3', value: 'Three', checked: true },
{ name: '-1', value: 'Many' },
],
},
handleChangeAnimation(checked) {
this.setData({ animation: checked });
},
handleChangeSwipeable(checked) {
this.setData({ swipeable: checked });
},
handleChangeSticky(checked) {
this.setData({ sticky: checked });
},
handleChangeTitleSlot(checked) {
this.setData({ titleSlot: checked });
},
handleChangePlusSlot(checked) {
this.setData({ plusSlot: checked });
},
handleChangeType(e) {
this.setData({
type: e.detail.value,
});
},
// Callback for tab clicks
handleChangeTab(e) {
this.setData({
index: e,
});
},
// Click event for the plus area in the upper-right corner
handleClickIcon() {
my.alert({
title: 'slot="plus"',
content: 'The icon in the custom slot was clicked',
});
},
handleChangeTabNum(e) {
if (e.detail.value === '1') {
this.setData({
tabs: [
{
title: 'Tab',
subTitle: 'Description',
badge: 6,
},
],
});
} else if (e.detail.value === '2') {
this.setData({
tabs: [
{
title: 'Tab',
subTitle: 'Description',
},
{
title: 'Tab 2',
subTitle: 'Description text',
},
],
});
} else if (e.detail.value === '3') {
this.setData({
tabs: [
{
title: 'Tab 1',
subTitle: 'Description',
},
{
title: 'Tab 2',
subTitle: 'Description text',
},
{
title: 'Tab 3',
subTitle: 'Description',
},
],
});
} else {
this.setData({
tabs: [
{
title: 'Tab 1',
subTitle: 'Description',
},
{
title: 'Tab 2',
subTitle: 'Description text',
},
{
title: 'Tab 3',
subTitle: 'Description',
},
{
title: '4 Tab',
subTitle: 'Description',
showBadge: true,
badge: 1,
},
{
title: '5 Tab',
subTitle: 'Description',
badge: 999,
},
{
title: '3 Tab',
subTitle: 'Description',
},
{
title: '4 Tab',
subTitle: 'Description',
},
{
title: '151111 Tab',
subTitle: 'Description',
},
{
title: '42345 Tab',
subTitle: 'Description',
},
{
title: '1511116787 Tab',
subTitle: 'Description',
},
{
title: '42452 Tab',
subTitle: 'Description',
},
{
title: '15451111 Tab',
},
{
title: '4234 Tab',
subTitle: 'Description',
},
{
title: '11251111 Tab',
subTitle: 'Description',
},
{
title: '44123 Tab',
},
{
title: '1531111 Tab',
subTitle: 'Description',
},
{
title: '41 Tab',
},
{
title: '15111111 Tab',
},
],
});
}
},
});
The following code is an example of `index.acss`:
.actions {
margin-top: 24px;
}
.amd-tabs-item-pane view {
background-color: #fff;
padding: 24rpx;
}
.plus-icon {
font-size: 48rpx;
}
The following code is an example of `index.json`:
{
"defaultTitle": "Tabs",
"usingComponents": {
"tabs": "antd-mini/es/Tabs/index",
"tab-content": "antd-mini/es/Tabs/TabItem/index",
"icon": "antd-mini/es/Icon/index",
"list": "antd-mini/es/List/index",
"list-item": "antd-mini/es/List/ListItem/index",
"switch": "antd-mini/es/Switch/index",
"demo-block": "../../components/DemoBlock/index"
}
}
fallback
The following code is an example of `index.axml`:
<tabs
index="{{curIdx}}"
type="{{type}}"
fallback="{{true}}"
sticky="{{sticky}}"
onChange="changeTab">
<block a:for="{{tabs}}">
<tab-content
style="{{curIdx===index ? '' : 'display:none'}}"
tab="{{item}}">
<view>
{{item.title}}
<view
a:for="{{height}}">
........Height adapts to content........
</view>
</view>
</tab-content>
</block>
</tabs>
The following code is an example of `index.js`:
const component2 = my.canIUse('component2');
Page({
data: {
curIdx: 2,
height: 30,
type: 'basis',
animation: false,
swipeable: false,
titleSlot: false,
plusSlot: true,
sticky: false,
tabs: [
{
title: 'Tab 1',
subTitle: 'Description',
corner: true,
},
{
title: 'Tab 2',
subTitle: 'Description text',
},
{
title: 'Tab 3',
subTitle: 'Description',
},
],
tabsType: [
{ name: 'basis', value: 'Basic', checked: true },
{ name: 'capsule', value: 'Capsule' },
{ name: 'mixin', value: 'With description' },
],
tabsNumber: [
{ name: '1', value: 'One' },
{ name: '2', value: 'Two' },
{ name: '3', value: 'Three' },
{ name: '-1', value: 'Many', checked: true },
],
tabsAnimation: [
{ name: true, value: 'true' },
{ name: false, value: 'false', checked: true },
],
tabsSwipeable: [
{ name: true, value: 'true' },
{ name: false, value: 'false', checked: true },
],
tabsTitleSlotScope: [
{ name: true, value: 'true' },
{ name: false, value: 'false', checked: true },
],
tabsSticky: [
{ name: true, value: 'true' },
{ name: false, value: 'false', checked: true },
],
tabsPlusSlotScope: [
{ name: true, value: 'true', checked: true },
{ name: false, value: 'false' },
],
canSwipe: true,
},
onLoad() {
if (!component2) {
this.setData({
canSwipe: component2,
});
}
},
// Callback for tab clicks
changeTab(e) {
this.setData({
curIdx: e,
});
},
// Click event for the plus area in the upper-right corner
iconClick() {
my.alert({
title: 'slot="plus"',
content: 'The icon in the custom slot was clicked',
});
},
});
The following code is an example of `index.json`:
{
"defaultTitle": "Tabs",
"usingComponents": {
"tabs": "antd-mini/es/Tabs/index",
"tab-content": "antd-mini/es/Tabs/TabItem/index",
"icon": "antd-mini/es/Icon/index"
}
}