shadcn-ui/ui · error · Error
useTheme must be used within a ThemeProvider
Error message
useTheme must be used within a ThemeProvider
What it means
This error is thrown by the useTheme hook when React.useContext(ThemeProviderContext) returns undefined. The context is created with a default value of undefined (line 22-24), so any component consuming useTheme that is NOT rendered as a descendant of <ThemeProvider> reads undefined and the guard at line 225 throws. It is the standard 'hook used outside its provider' fail-fast pattern that prevents silently operating on missing theme state.
Source
Thrown at templates/vite-monorepo/apps/web/src/components/theme-provider.tsx:226
() => ({
theme,
setTheme,
}),
[theme, setTheme]
)
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
)
}
export const useTheme = () => {
const context = React.useContext(ThemeProviderContext)
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
View on GitHub (pinned to efac598707)
Solutions
- Ensure the component calling useTheme is a descendant of <ThemeProvider> in the React tree. Move it inside the provider in your entry file, e.g. wrap <App/> with <ThemeProvider defaultTheme="system" storageKey="theme"> so every consumer sits beneath it.
- If the error surfaces in tests, wrap the rendered component: render(<ThemeProvider><MyComponent/></ThemeProvider>) in Vitest/RTL, or add a global decorator in Storybook (parameters.decorators) that supplies ThemeProvider.
- If a portal or separate React root needs the theme, either render it within the existing provider tree, or mount an additional <ThemeProvider> around that secondary root.
- Provide a safe fallback by guarding before calling useTheme: check React.useContext(ThemeProviderContext) directly and supply a default instead of throwing, if a missing provider is an expected state.
- Remove the useTheme call from components that do not strictly need live theme state, or pass theme/setTheme down as explicit props from a component that is already inside the provider.
Example fix
// before (main.tsx) — App renders above/without ThemeProvider
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
// after — ThemeProvider wraps the entire consuming tree
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ThemeProvider defaultTheme="system" storageKey="theme">
<App />
</ThemeProvider>
</React.StrictMode>
) Defensive patterns
Strategy: validation
Validate before calling
import * as React from 'react'
import { ThemeProviderContext } from './theme-provider'
function useThemeOptional(defaultValue = { theme: 'system' as const, setTheme: () => {} }) {
const ctx = React.useContext(ThemeProviderContext)
if (ctx === undefined) {
// Caller is outside ThemeProvider; avoid throwing by returning a default.
return defaultValue
}
return ctx
} Type guard
import * as React from 'react'
import { ThemeProviderContext, type ThemeProviderState } from './theme-provider'
function hasThemeProvider(ctx: unknown): ctx is ThemeProviderState {
return (
typeof ctx === 'object' &&
ctx !== null &&
'theme' in ctx &&
typeof (ctx as ThemeProviderState).setTheme === 'function'
)
}
// usage:
// const ctx = React.useContext(ThemeProviderContext)
// if (!hasThemeProvider(ctx)) return null // or render a fallback Try / catch
// React render throws are not catchable by try/catch around useTheme.
// Guard at the component boundary instead:
function MyComponent() {
const ctx = React.useContext(ThemeProviderContext)
if (ctx === undefined) {
// Render a non-theme-dependent fallback rather than calling useTheme
return <div>Theme unavailable</div>
}
// ctx is now narrowed to ThemeProviderState
return <div>Current theme: {ctx.theme}</div>
} Prevention
- Establish a single root-level <ThemeProvider> in the app entry (main.tsx) that wraps the entire component tree so every consumer is guaranteed a provider.
- Add a test helper/decorator (Vitest RTL wrapper, Storybook decorator) that mounts ThemeProvider around any component under test that calls useTheme.
- Audit new components during code review: any useTheme import must trace back to a node inside the provider subtree.
- For portals and secondary roots, either re-mount ThemeProvider or consume the context from the original tree via a ref/props instead of a fresh useTheme call.
- If you need graceful degradation, consume ThemeProviderContext directly and supply a fallback rather than using the throwing useTheme hook.
When it happens
Trigger: Calling useTheme() in a component that is rendered outside the <ThemeProvider> subtree. Concrete cases in this template: (1) a component placed in main.tsx or App.tsx ABOVE or as a sibling to <ThemeProvider>, (2) a component rendered through a portal whose React tree parent is not ThemeProvider, (3) using useTheme in a Storybook story or unit test (Vitest/RTL) without wrapping the component in ThemeProvider, (4) a second root created with ReactDOM.createRoot that is not wrapped, (5) an error boundary's fallback component that calls useTheme but is mounted outside the provider.
Common situations: Adding a new component that needs the current theme but forgetting to place it inside the provider tree; restructuring the app entry (main.tsx) and accidentally moving ThemeProvider below the component; testing components in isolation without the provider decorator; SSR or pre-render paths where a theme-consuming component renders before ThemeProvider mounts; refactoring that splits the tree into multiple roots; upgrading a shadcn-style theme setup and losing the provider wrapper.
Related errors
- ${component} must be used within a Questionnaire.Root compon
- ${component} must be used within a Questionnaire.Choice comp
- ${component} must be used within a Questionnaire.Item compon
- useThemeConfig must be used within an ActiveThemeProvider
- usePreviewOverride must be used within a PreviewOverrideProv
AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12).
Data as JSON: /api/errors/738dcb7ad5b5b192.
Report an issue: GitHub.