diff --git a/packages/angular/standalone/src/index.ts b/packages/angular/standalone/src/index.ts
index b2e8af14fd..8bd5de65c8 100644
--- a/packages/angular/standalone/src/index.ts
+++ b/packages/angular/standalone/src/index.ts
@@ -3,6 +3,7 @@ export { IonModal } from './overlays/modal';
export { IonPopover } from './overlays/popover';
export { IonRouterOutlet } from './navigation/router-outlet';
export { IonRouterLink, IonRouterLinkWithHref } from './navigation/router-link-delegate';
+export { IonTabs } from './navigation/tabs';
export { provideIonicAngular } from './providers/ionic-angular';
export {
ActionSheetController,
diff --git a/packages/angular/standalone/src/navigation/tabs.ts b/packages/angular/standalone/src/navigation/tabs.ts
new file mode 100644
index 0000000000..b54be17934
--- /dev/null
+++ b/packages/angular/standalone/src/navigation/tabs.ts
@@ -0,0 +1,52 @@
+import { Component, ContentChild, ContentChildren, ViewChild, QueryList } from '@angular/core';
+import { IonTabs as IonTabsBase } from '@ionic/angular/common';
+
+import { IonTabBar } from '../directives/proxies';
+
+import { IonRouterOutlet } from './router-outlet';
+
+@Component({
+ selector: 'ion-tabs',
+ template: `
+
+
+
+
+
+ `,
+ standalone: true,
+ styles: [
+ `
+ :host {
+ display: flex;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ flex-direction: column;
+
+ width: 100%;
+ height: 100%;
+
+ contain: layout size style;
+ }
+ .tabs-inner {
+ position: relative;
+
+ flex: 1;
+
+ contain: layout size style;
+ }
+ `,
+ ],
+ imports: [IonRouterOutlet],
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class IonTabs extends IonTabsBase {
+ @ViewChild('outlet', { read: IonRouterOutlet, static: false }) outlet: IonRouterOutlet;
+
+ @ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
+ @ContentChildren(IonTabBar) tabBars: QueryList;
+}
diff --git a/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts b/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts
new file mode 100644
index 0000000000..a0558f299f
--- /dev/null
+++ b/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts
@@ -0,0 +1,16 @@
+describe('Tabs', () => {
+ beforeEach(() => {
+ cy.visit('/standalone/tabs');
+ });
+
+ it('should redirect to the default tab', () => {
+ cy.get('app-tab-one').should('be.visible');
+ cy.contains('Tab 1');
+ });
+
+ it('should render new content when switching tabs', () => {
+ cy.get('#tab-button-tab-two').click();
+ cy.get('app-tab-two').should('be.visible');
+ cy.contains('Tab 2');
+ });
+});
diff --git a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
index 2367c61772..d97d34c66b 100644
--- a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
+++ b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts
@@ -16,6 +16,16 @@ export const routes: Routes = [
{ path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(c => c.OverlayControllersComponent) },
{ path: 'button', loadComponent: () => import('../button/button.component').then(c => c.ButtonComponent) },
{ path: 'icon', loadComponent: () => import('../icon/icon.component').then(c => c.IconComponent) },
+ { path: 'tabs', redirectTo: '/standalone/tabs/tab-one', pathMatch: 'full' },
+ {
+ path: 'tabs',
+ loadComponent: () => import('../tabs/tabs.component').then(c => c.TabsComponent),
+ children: [
+ { path: 'tab-one', loadComponent: () => import('../tabs/tab1.component').then(c => c.TabOneComponent) },
+ { path: 'tab-two', loadComponent: () => import('../tabs/tab2.component').then(c => c.TabTwoComponent) },
+ { path: 'tab-three', loadComponent: () => import('../tabs/tab3.component').then(c => c.TabThreeComponent) }
+ ]
+ },
]
},
];
diff --git a/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts b/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts
new file mode 100644
index 0000000000..8b6e8976ec
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-tab-one',
+ template: `
+ Tab 1
+ `,
+ standalone: true,
+})
+export class TabOneComponent {
+
+}
diff --git a/packages/angular/test/base/src/app/standalone/tabs/tab2.component.ts b/packages/angular/test/base/src/app/standalone/tabs/tab2.component.ts
new file mode 100644
index 0000000000..b27b301fdd
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/tabs/tab2.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-tab-two',
+ template: `
+ Tab 2
+ `,
+ standalone: true,
+})
+export class TabTwoComponent {
+
+}
diff --git a/packages/angular/test/base/src/app/standalone/tabs/tab3.component.ts b/packages/angular/test/base/src/app/standalone/tabs/tab3.component.ts
new file mode 100644
index 0000000000..b2af4e6550
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/tabs/tab3.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-tab-three',
+ template: `
+ Tab 3
+ `,
+ standalone: true,
+})
+export class TabThreeComponent {
+
+}
diff --git a/packages/angular/test/base/src/app/standalone/tabs/tabs.component.html b/packages/angular/test/base/src/app/standalone/tabs/tabs.component.html
new file mode 100644
index 0000000000..02105307ce
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/tabs/tabs.component.html
@@ -0,0 +1,42 @@
+
+
+
+ Tab One
+
+
+
+
+ Tab Two
+
+
+
+
+ Tab Three
+
+
+
+
+
+
+ -
+ ionTabsWillChange counter: {{ tabsWillChangeCounter }}
+
+ -
+ ionTabsWillChange event: {{ tabsWillChangeEvent }}
+
+ -
+ ionTabsWillChange selectedTab: {{ tabsWillChangeSelectedTab }}
+
+
+
+ -
+ ionTabsDidChange counter: {{ tabsDidChangeCounter }}
+
+ -
+ ionTabsDidChange event: {{ tabsDidChangeEvent }}
+
+ -
+ ionTabsDidChange selectedTab: {{ tabsDidChangeSelectedTab }}
+
+
+
\ No newline at end of file
diff --git a/packages/angular/test/base/src/app/standalone/tabs/tabs.component.ts b/packages/angular/test/base/src/app/standalone/tabs/tabs.component.ts
new file mode 100644
index 0000000000..d4b889e0dd
--- /dev/null
+++ b/packages/angular/test/base/src/app/standalone/tabs/tabs.component.ts
@@ -0,0 +1,38 @@
+import { Component, ViewChild } from '@angular/core';
+import { IonTabBar, IonTabButton, IonIcon, IonLabel, IonTabs } from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { add, logoIonic, save } from 'ionicons/icons';
+
+addIcons({ add, logoIonic, save });
+
+@Component({
+ selector: 'app-tabs',
+ templateUrl: './tabs.component.html',
+ standalone: true,
+ imports: [IonTabBar, IonTabButton, IonIcon, IonLabel, IonTabs]
+})
+export class TabsComponent {
+ tabsDidChangeCounter = 0;
+ tabsDidChangeEvent = '';
+ tabsDidChangeSelectedTab? = '';
+
+ tabsWillChangeCounter = 0;
+ tabsWillChangeEvent = '';
+ tabsWillChangeSelectedTab? = '';
+
+ @ViewChild(IonTabBar) tabBar!: IonTabBar;
+
+ tabChanged(ev: { tab: string }) {
+ console.log('ionTabsDidChange', this.tabBar.selectedTab);
+ this.tabsDidChangeCounter++;
+ this.tabsDidChangeEvent = ev.tab;
+ this.tabsDidChangeSelectedTab = this.tabBar.selectedTab;
+ }
+
+ tabsWillChange(ev: { tab: string }) {
+ console.log('ionTabsWillChange', this.tabBar.selectedTab);
+ this.tabsWillChangeCounter++;
+ this.tabsWillChangeEvent = ev.tab;
+ this.tabsWillChangeSelectedTab = this.tabBar.selectedTab;
+ }
+}