mui/material-ui · error · TypeError

No TabContext provided

Error message

No TabContext provided

What it means

TabList from @mui/lab reads shared tab state via useTabContext(); the context returns null (not undefined) when no <TabContext> ancestor is present, and TabList throws a TypeError immediately because it needs context.value to wire aria-controls/ids on its Tab children. It is a hard precondition — TabList cannot render without the context.

Source

Thrown at packages/mui-lab/src/TabList/TabList.js:11

'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '@mui/material/Tabs';
import { useTabContext, getTabId, getPanelId } from '../TabContext';

const TabList = React.forwardRef(function TabList(props, ref) {
  const { children: childrenProp, ...other } = props;
  const context = useTabContext();
  if (context === null) {
    throw new TypeError('No TabContext provided');
  }
  const children = React.Children.map(childrenProp, (child) => {
    if (!React.isValidElement(child)) {
      return null;
    }

    return React.cloneElement(child, {
      // SOMEDAY: `Tabs` will set those themselves
      'aria-controls': getPanelId(context, child.props.value),
      id: getTabId(context, child.props.value),
    });
  });

  return (
    <Tabs {...other} ref={ref} value={context.value}>
      {children}
    </Tabs>
  );

View on GitHub (pinned to bdc96df2cb)

Solutions

  1. Wrap <TabList> (and its sibling <TabPanel>s) in <TabContext value={value}> so useTabContext returns the context object.
  2. Ensure the same value state is shared by TabContext, TabList, and TabPanel.
  3. If you do not need the composable API, switch to @mui/material <Tabs> which manages its own state.

Example fix

// before
<TabList onChange={handleChange}>
  <Tab label="One" value="1" />
</TabList>
// after
<TabContext value={value}>
  <TabList onChange={handleChange}>
    <Tab label="One" value="1" />
  </TabList>
</TabContext>
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a TabContext ancestor exists before mounting TabList.
import { useContext } from 'react';
import { TabContext } from '@mui/lab';
function useHasTabContext() {
  return useContext(TabContext) !== null;
}

Type guard

import { Context as TabContextReactContext } from '@mui/lab/TabContext'; // pseudo
// In practice, treat presence of <TabContext> in the JSX tree as the guard:
function isInsideTabContext(): boolean { /* use useContext(TabContext) */ return true; }

Prevention

When it happens

Trigger: Rendering <TabList> directly without wrapping it in a <TabContext value={value}>. Common when copy-pasting only the TabList/Tab/TabPanel pieces from a lab tabs example, or when refactoring and accidentally removing the TabContext wrapper.

Common situations: Migrating from @mui/material Tabs to the @mui/lab TabContext/TabList/TabPanel composable API and forgetting the context; conditional rendering that mounts TabList in a branch where TabContext was not rendered.

Related errors


AI-assisted analysis of mui/material-ui@bdc96df2cb (2026-08-12). Data as JSON: /api/errors/112998e1e9ad8d8b. Report an issue: GitHub.