ionic-team/ionic-framework · error · Error
IonTabs cannot contain an IonRouterOutlet and IonTab at the
Error message
IonTabs cannot contain an IonRouterOutlet and IonTab at the same time.
What it means
Thrown from Vue `<IonTabs>` render() when the slot contains BOTH an `IonRouterOutlet` and an `IonTab`. These are mutually exclusive navigation models: IonTabs must use either the router outlet (URL/history-based) OR plain `IonTab` children (router-less), never both. The check exists to prevent ambiguous tab state.
Source
Thrown at packages/vue/src/components/IonTabs.ts:144
routerOutlet = slottedContent.find((child: VNode) =>
isRouterOutlet(child)
);
/**
* Developers must pass at least one ion-tab
* inside of ion-tabs if they want to use a
* basic tab-based navigation without the
* history stack or URL updates associated
* with the router.
*/
hasTab = slottedContent.some((child: VNode) => isTab(child));
}
if (!routerOutlet && !hasTab) {
throw new Error("IonTabs must contain an IonRouterOutlet or an IonTab.");
}
if (routerOutlet && hasTab) {
throw new Error(
"IonTabs cannot contain an IonRouterOutlet and IonTab at the same time."
);
}
if (hasTab) {
return h(
"ion-tabs",
{
...props,
},
slottedContent
);
}
/**
* TODO(ROU-11056)
*
* Vue handles the error case for when there is noView on GitHub (pinned to 625f9c38ad)
Solutions
- Pick one navigation model: keep `<IonRouterOutlet>` and remove all `<IonTab>` children, OR keep `<IonTab>` children and remove the outlet.
- Search the `<IonTabs>` template for `IonTab`/`ion-tab` and `IonRouterOutlet`/`ion-router-outlet` and remove the unwanted one.
- If using router-less tabs, drive tab content via `<IonTab>` components and an `<IonTabBar>` without an outlet.
- If using router tabs, declare routes inside `<IonRouterOutlet>` and do not author `<IonTab>` elements.
Example fix
<!-- before (both present) --> <IonTabs> <IonRouterOutlet>...</IonRouterOutlet> <IonTab tab="home">...</IonTab> </IonTabs> <!-- after (router model only) --> <IonTabs> <IonRouterOutlet>...</IonRouterOutlet> <IonTabBar slot="bottom">...</IonTabBar> </IonTabs>
Defensive patterns
Strategy: type-guard
Validate before calling
const outlet = slotted?.some((n) => n?.type && ((n.type as any).name === 'IonRouterOutlet' || n.type === 'ion-router-outlet'));
const tab = slotted?.some((n) => n?.type && ((n.type as any).name === 'ion-tab' || n.type === 'IonTab'));
if (outlet && tab) {
throw new Error('Choose one: IonRouterOutlet (router tabs) OR IonTab (router-less tabs), not both.');
} Type guard
function isExclusiveTabsModel(slotted: any[] | undefined): boolean {
const outlet = !!slotted?.some((n) => n?.type && ((n.type as any).name === 'IonRouterOutlet' || n.type === 'ion-router-outlet'));
const tab = !!slotted?.some((n) => n?.type && ((n.type as any).name === 'ion-tab' || n.type === 'IonTab'));
return outlet !== tab; // true only when exactly one is present
} Prevention
- Decide the tab navigation model up front and keep templates consistent.
- During migration, remove the old shape when adding the new one.
- Code-review tab templates for any leftover IonTab or IonRouterOutlet.
When it happens
Trigger: Authoring `<IonTabs>` that simultaneously includes `<IonRouterOutlet>` (or `ion-router-outlet`) and one or more `<IonTab>` (`ion-tab`) descendants anywhere in the slotted VNodes, including inside a Fragment/v-for.
Common situations: Partially migrating from router-less tabs to router tabs and leaving an `<IonTab>` behind; combining a router outlet with manually-declared tab children in the same template; tabs generated from a list where both shapes are present.
Related errors
- IonTabs must contain an IonRouterOutlet or an IonTab.
- IonTabs must contain an IonRouterOutlet or an IonTab
- IonTabs cannot contain an IonRouterOutlet and an IonTab at t
- Cannot activate an already activated outlet
- no views in the stack to be removed
AI-assisted analysis of ionic-team/ionic-framework@625f9c38ad (2026-08-12).
Data as JSON: /api/errors/b91d7456be7f2b43.
Report an issue: GitHub.