ionic-team/ionic-framework · error · Error
IonTabs must contain an IonRouterOutlet or an IonTab.
Error message
IonTabs must contain an IonRouterOutlet or an IonTab.
What it means
Thrown from the render() of the Vue `<IonTabs>` component when the default slot contains neither an `IonRouterOutlet` nor an `IonTab` vnode. IonTabs requires exactly one of those two navigation models so it knows how to manage child views. Because this fires during render, Vue surfaces it as an uncaught runtime error.
Source
Thrown at packages/vue/src/components/IonTabs.ts:141
* the history stack or URL updates associated
* with the router.
*/
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
);
}
/**View on GitHub (pinned to 625f9c38ad)
Solutions
- Add an `<ion-router-outlet>` (via `<IonRouterOutlet>`) as a direct child of `<IonTabs>` for router-driven tabs.
- Or add at least one `<IonTab>` child for router-less tab navigation.
- Ensure the outlet/tab is not gated behind a `v-if` that evaluates false on the first render; use `v-show` or render a fallback.
- If the slot content comes from a dynamic list, guarantee the list is non-empty and contains a recognized tab/outlet element.
Example fix
<!-- before -->
<IonTabs>
<IonTabBar slot="bottom">
<IonTabButton tab="home"><IonLabel>Home</IonLabel></IonTabButton>
</IonTabBar>
</IonTabs>
<!-- after -->
<IonTabs>
<IonRouterOutlet>
<Route path="/home" component="Home" />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="home" href="/home"><IonLabel>Home</IonLabel></IonTabButton>
</IonTabBar>
</IonTabs> Defensive patterns
Strategy: type-guard
Validate before calling
// Before relying on IonTabs, assert the slot will contain a recognized child
import { h, Fragment, isVNode } from 'vue';
const isRouterOutlet = (n: any) => n?.type && ((n.type as any).name === 'IonRouterOutlet' || n.type === 'ion-router-outlet');
const isTab = (n: any) => {
if (n?.type === Fragment && Array.isArray(n.children)) return n.children.some((c) => isVNode(c) && isTab(c));
return n?.type && ((n.type as any).name === 'ion-tab' || n.type === IonTab);
};
function validateTabsChildren(slotted: any[] | undefined) {
const hasOutlet = !!slotted?.some(isRouterOutlet);
const hasTab = !!slotted?.some(isTab);
if (!hasOutlet && !hasTab) throw new Error('IonTabs needs an IonRouterOutlet or IonTab child');
} Type guard
function hasTabsNavModel(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 Boolean(outlet) || Boolean(tab);
} Prevention
- Always include an <IonRouterOutlet> (router tabs) or at least one <IonTab> (router-less tabs) inside <IonTabs>.
- Avoid v-if on the outlet/tab that can be false on first render; use v-show or a guaranteed fallback.
- Keep tab templates in one place to spot missing outlets during review.
When it happens
Trigger: Authoring `<IonTabs>` whose children are only an `<IonTabBar>`, plain divs, or nothing at all — i.e. neither `<IonRouterOutlet>` nor `<IonTab>` is present in the slot.
Common situations: Migrating from an older tabs API that allowed a tab bar alone; forgetting to wrap routes in `<IonRouterOutlet>`; rendering tabs conditionally so the outlet is absent on first render; copy-pasting a tabs template that omitted the outlet.
Related errors
- IonTabs cannot contain an IonRouterOutlet and IonTab at the
- 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/56c2794fbcf711e3.
Report an issue: GitHub.