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 during the render of the React `<IonTabs>` component after it iterates children with `React.Children.forEach`. If no child resolves to an `IonRouterOutlet` (by type or `isRouterOutlet`) and no child resolves to an `IonTab`, the component refuses to render because it cannot determine the navigation model. Thrown during render, so React treats it as an uncaught error.
Source
Thrown at packages/react/src/components/navigation/IonTabs.tsx:178
if (onIonTabsDidChange !== undefined) {
childProps = {
...childProps,
onIonTabsDidChange,
};
}
if (onIonTabsWillChange !== undefined) {
childProps = {
...childProps,
onIonTabsWillChange,
};
}
this.ionTabContextState.tabBarProps = childProps;
});
if (!outlet && !hasTab) {
throw new Error('IonTabs must contain an IonRouterOutlet or an IonTab');
}
if (outlet && hasTab) {
throw new Error('IonTabs cannot contain an IonRouterOutlet and an IonTab at the same time');
}
if (hasTab) {
return <IonTabsInner {...this.props}></IonTabsInner>;
}
/**
* TODO(ROU-11051)
*
* There is no error handling for the case where there
* is no associated Route for the given IonTabButton.
*
* More investigation is needed to determine how to
* handle this to prevent any overwriting of the
* IonTabButton's onClick handler and how the routingView on GitHub (pinned to 625f9c38ad)
Solutions
- Add `<IonRouterOutlet>` (with `<Route>` children) as a direct child of `<IonTabs>` for router-driven tabs.
- Or add at least one `<IonTab>` child for router-less tabs.
- Make sure the outlet is a direct, statically-present child — avoid `condition && <IonRouterOutlet/>` that is false on first render.
- If using a render-prop child, ensure the function returns a tree containing an `<IonRouterOutlet>` or `<IonTab>`.
Example fix
// before
<IonTabs>
<IonTabBar slot="bottom">
<IonTabButton tab="home"><IonLabel>Home</IonLabel></IonTabButton>
</IonTabBar>
</IonTabs>
// after
<IonTabs>
<IonRouterOutlet>
<Route exact path="/home" component={Home} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="home" href="/home"><IonLabel>Home</IonLabel></IonTabButton>
</IonTabBar>
</IonTabs> Defensive patterns
Strategy: validation
Validate before calling
import { Fragment, isValidElement } from 'react';
import { IonRouterOutlet, IonTab } from '@ionic/react';
function validateIonTabsChildren(children: React.ReactNode) {
let outlet = false, hasTab = false;
React.Children.forEach(children, (child: any) => {
if (child == null || typeof child !== 'object' || !('type' in child)) return;
if (child.type === IonRouterOutlet || child.type?.isRouterOutlet) outlet = true;
else if (child.type === Fragment && child.props?.children?.[0]?.type === IonRouterOutlet) outlet = true;
else if (child.type === IonTab) hasTab = true;
});
if (!outlet && !hasTab) throw new Error('IonTabs needs an IonRouterOutlet or IonTab child');
} Type guard
function hasTabsNavModel(children: React.ReactNode): boolean {
let outlet = false, hasTab = false;
React.Children.forEach(children, (child: any) => {
if (child?.type === IonRouterOutlet || child?.type?.isRouterOutlet) outlet = true;
else if (child?.type === IonTab) hasTab = true;
});
return outlet || hasTab;
} Prevention
- Always render <IonRouterOutlet> or an <IonTab> as a direct child of <IonTabs>.
- Avoid conditionally rendering the outlet on first render; use a guaranteed fallback.
- When using a render-prop child, ensure it returns a tree containing the outlet or a tab.
When it happens
Trigger: Authoring `<IonTabs>` whose children are only `<IonTabBar>`, fragments without an outlet, plain elements, or `null` — i.e. neither an `<IonRouterOutlet>` nor an `<IonTab>` is among the direct children. Also triggered when children are passed as a function that returns no outlet/tab.
Common situations: Following an outdated tutorial that omitted the outlet; conditionally rendering the outlet such that it is absent on first render; tabs defined purely as a tab bar with routes declared elsewhere; refactor that moved the outlet into a child component whose type is not recognized.
Related errors
- IonTabs cannot contain an IonRouterOutlet and an IonTab at t
- IonTabs must contain an IonRouterOutlet or an IonTab.
- IonTabs cannot contain an IonRouterOutlet and IonTab at the
- 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/80187fe152bcd290.
Report an issue: GitHub.