I am trying to implement dual theme (dark & light) in Next JS using MUI using use context method.
It gives me this error:
⨯ srcapppage.tsx (6:50) @ useThemeContext
⨯ TypeError: (0 , _providers_theme_context_provider__WEBPACK_IMPORTED_MODULE_1__.useThemeContext) is not a function
at Home (./src/app/page.tsx:16:117)
at stringify (<anonymous>)
digest: "3545525166"
Kindly help me to resolve this issue.
My @/theme/index.tsx
'use client';
import { createTheme, responsiveFontSizes } from '@mui/material/styles';
import { lightPalette, darkPalette } from './palette';
export const lightTheme = responsiveFontSizes(createTheme({
typography: {
fontFamily: 'var(--font-roboto)',
},
palette: lightPalette,
}));
export const darkTheme = responsiveFontSizes(createTheme({
typography: {
fontFamily: 'var(--font-roboto)',
},
palette: darkPalette
}));
I am trying to use context provider method by implementing in file @/providers/theme-context-provider.tsx
:
// @/providers/theme-context-provider.tsx
'use client'; // Required for client-side hooks
import { createContext, useContext, useState, useMemo, useEffect, ReactNode } from 'react';
import { ThemeProvider, CssBaseline } from '@mui/material';
import { lightTheme, darkTheme } from '@/theme'; // Adjusted import path
// Define the shape of the context
interface ThemeContextType {
toggleTheme: () => void;
mode: 'light' | 'dark';
}
// Create a context to store theme-related information
const ThemeContext = createContext<ThemeContextType>({
toggleTheme: () => {},
mode: 'light', // Default value
});
// Custom hook to use the ThemeContext
export const useThemeContext = () => useContext(ThemeContext);
interface ThemeContextProviderProps {
children: ReactNode;
}
// Create the provider component that will wrap your app
export const ThemeContextProvider: React.FC<ThemeContextProviderProps> = ({ children }) => {
// Check for user's preferred color scheme
const prefersDarkMode = typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches;
const [mode, setMode] = useState<'light' | 'dark'>(prefersDarkMode ? 'dark' : 'light');
// Sync with system theme changes
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e: MediaQueryListEvent) => setMode(e.matches ? 'dark' : 'light');
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, []);
// Handle manual theme toggle
const toggleTheme = () => {
const newMode = mode === 'light' ? 'dark' : 'light';
setMode(newMode);
localStorage.setItem('theme', newMode); // Persist the theme in localStorage
};
// Memoize the current theme
const currentTheme = useMemo(() => (mode === 'light' ? lightTheme : darkTheme), [mode]);
return (
<ThemeContext.Provider value={{ toggleTheme, mode }}>
<ThemeProvider theme={currentTheme}>
<CssBaseline />
{children}
</ThemeProvider>
</ThemeContext.Provider>
);
};
I used this provider in @/app/layout.tsx
:
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter'
import { ThemeContextProvider } from "@/providers/theme-context-provider";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<ThemeContextProvider>
{children}
</ThemeContextProvider>
</AppRouterCacheProvider>
</body>
</html>
);
}
But when i try to use this context in my @/app/page.tsx
// app/page.tsx
import { Box, Button, Typography } from "@mui/material";
import { useThemeContext } from "@/providers/theme-context-provider";
export default function Home() {
const { toggleTheme, mode } = useThemeContext();
return (
<Box padding={2}>
<Typography variant="h4" gutterBottom>
Hello, welcome to my Next.js app!
</Typography>
<Button variant="contained" color="primary">
HELLO
</Button>
<Button variant="contained" color="primary" onClick={toggleTheme}>
Toggle to {mode === 'light' ? 'Dark' : 'Light'} Mode
</Button>
</Box>
);
}
2
Answers
'use client'
inside the hook file itself; simply remove'use client'
from@/providers/theme-context-provider.tsx
.<ThemeContextProvider>
must be used within aClient Component
. To achieve this, create a new component namedClientProviders
that wraps<ThemeContextProvider>
(include'use client'
inClientProviders
).@/app/page.tsx
file should also be aClient Component
(include'use client'
) because it relies onuseThemeContext
.useThemeContext();
should be used in the client page so you need wrap the your button in to client componant … then it will work