emotion-js/emotion · error · Error

[ThemeProvider] Please make your theme prop a plain object

Error message

[ThemeProvider] Please make your theme prop a plain object

What it means

getTheme() in @emotion/react's ThemeProvider validates the theme prop itself: it must be a non-null, non-array object (or a function returning one). Passing null, undefined, a string, a number, or an array as the theme prop triggers this development-mode error. The library needs a spreadable object to merge with the outer theme.

Source

Thrown at packages/react/src/theming.tsx:52

  if (typeof theme === 'function') {
    const mergedTheme = theme(outerTheme)
    if (
      isDevelopment &&
      (mergedTheme == null ||
        typeof mergedTheme !== 'object' ||
        Array.isArray(mergedTheme))
    ) {
      throw new Error(
        '[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!'
      )
    }
    return mergedTheme
  }
  if (
    isDevelopment &&
    (theme == null || typeof theme !== 'object' || Array.isArray(theme))
  ) {
    throw new Error(
      '[ThemeProvider] Please make your theme prop a plain object'
    )
  }

  return { ...outerTheme, ...theme }
}

let createCacheWithTheme = /* #__PURE__ */ weakMemoize((outerTheme: Theme) => {
  return weakMemoize((theme: Partial<Theme> | ((theme: Theme) => Theme)) => {
    return getTheme(outerTheme, theme)
  })
})

export interface ThemeProviderProps {
  theme: Partial<Theme> | ((outerTheme: Theme) => Theme)
  children: React.ReactNode
}

View on GitHub (pinned to b882bcba85)

Solutions

  1. Initialize theme state to an object, e.g. useState({}) instead of useState(null)
  2. Pass theme={theme ?? {}} to guard against null/undefined
  3. Convert string themes to objects: theme={themes[name]} where themes is a record of objects
  4. If representing multiple themes, merge them into one object rather than passing an array

Example fix

// before
<ThemeProvider theme={themeLoading ? null : currentTheme}>
// after
<ThemeProvider theme={themeLoading ? {} : currentTheme}>
Defensive patterns

Strategy: type-guard

Validate before calling

if (theme == null || typeof theme !== 'object' || Array.isArray(theme)) {
  throw new TypeError('ThemeProvider requires a plain object theme')
}

Type guard

function isThemeObject(t: unknown): t is Record<string, unknown> {
  return typeof t === 'object' && t !== null && !Array.isArray(t)
}

Try / catch

try {
  render(<ThemeProvider theme={theme}>{children}</ThemeProvider>)
} catch (e) {
  if (String(e.message).includes('plain object')) {
    render(<ThemeProvider theme={{}}>{children}</ThemeProvider>)
  } else throw e
}

Prevention

When it happens

Trigger: <ThemeProvider theme={null}>, theme={undefined}, theme={'dark'}, theme={[...]} — any non-object, non-function value for the theme prop, evaluated when isDevelopment. Also spreading an array as theme via { ...outerTheme, ...theme } is explicitly blocked.

Common situations: State that holds the theme initialized to null before mounting (theme={themeState}); passing a theme string like 'dark' instead of an object; mapping an array of theme tokens directly as the theme; optional chaining yielding undefined.

Related errors


AI-assisted analysis of emotion-js/emotion@b882bcba85 (2026-09-02). Data as JSON: /api/errors/2e46c6d21a76bcce. Report an issue: GitHub.