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 routing

View on GitHub (pinned to 625f9c38ad)

Solutions

  1. Add `<IonRouterOutlet>` (with `<Route>` children) as a direct child of `<IonTabs>` for router-driven tabs.
  2. Or add at least one `<IonTab>` child for router-less tabs.
  3. Make sure the outlet is a direct, statically-present child — avoid `condition && <IonRouterOutlet/>` that is false on first render.
  4. 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

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


AI-assisted analysis of ionic-team/ionic-framework@625f9c38ad (2026-08-12). Data as JSON: /api/errors/80187fe152bcd290. Report an issue: GitHub.