mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.59k stars 32.21k forks source link

useTheme() hook is not returning palette colors #32806

Open peterzagoranski opened 2 years ago

peterzagoranski commented 2 years ago

Duplicates

Latest version

Current behavior 😯

`const theme = useTheme();

console.log(theme.palette); // => {mode: 'light'}`

Expected behavior 🤔

`const theme = useTheme();

console.log(theme.palette); // => {mode: 'light', primary: { ... }, secondary: { ... }, ... }`

Steps to reproduce 🕹

No response

Context 🔦

No response

Your environment 🌎

@emotion/react: ^11.9.0 => 11.9.0
@emotion/styled: ^11.8.1 => 11.8.1
@mui/base:  5.0.0-alpha.81
@mui/icons-material:  5.6.2
@mui/lab: 5.0.0-alpha.74 => 5.0.0-alpha.74
@mui/material: ^5.8.0 => 5.8.0
@mui/private-theming:  5.6.2
@mui/styled-engine:  5.6.1
@mui/styles:  5.6.2
@mui/system:  5.6.4
@mui/types:  7.1.3
@mui/utils:  5.6.1
@types/react:  18.0.9
react: ^18.1.0 => 18.1.0
react-dom: ^18.1.0 => 18.1.0
peterzagoranski commented 2 years ago

I found what was the problem ... I had to replace:

import { useTheme } from '@mui/system';

with:

import { useTheme } from '@mui/material';

This was fine before version 5.7.0 ...

siriwatknp commented 2 years ago

I found what was the problem ... I had to replace:

import { useTheme } from '@mui/system';

with:

import { useTheme } from '@mui/material';

This was fine before version 5.7.0 ...

Strange, it should work the same. Could you provide a CodeSandbox that I can reproduce?

totszwai commented 2 years ago

I think there is a problem with useTheme it is returning the systemDefaultTheme and not the custom theme object that is currently in the ThemeProvider.

When I debug my code, Mui's useTheme was going into here: https://github.com/mui/material-ui/blob/512896973499adbbda057e7f3685d1b23cc02de9/packages/mui-system/src/useTheme.js#L4

My expectation is that, it would supposed to go in this different useTheme instead and grab the proper theme object from the context? https://github.com/mui/material-ui/blob/c1936ad4764f84f9e1788af79daef7280cc7b4da/packages/mui-private-theming/src/useTheme/useTheme.js#L5

I have code like this, my component tried to grab the theme using useTheme:

import { useTheme } from '@mui/material/styles';

const Blah = () => {
  const theme = useTheme<MyCustomThemePalette>();
  // this returns the default theme object without any of my custom definition
  console.log(theme);
}

I have the following custom provider which, when dumping the incoming theme object, it has everything I defined, but not in the children.

import { ThemeProvider as MuiThemeProvider, CssBaseline, StyledEngineProvider } from '@mui/material';
import { ThemeProvider } from 'styled-components';

export const MyThemeProvider = (props: React.PropsWithChildren<MyThemeOptions>) => {
  let theme = props.theme;
  console.debug('[MyThemeProvider] theme?', theme);

  return (
    <StyledEngineProvider injectFirst>
      <MuiThemeProvider theme={props.theme}>
        <ThemeProvider theme={props.theme}>
          <>
            <GlobalStyle />
            <CssBaseline />
            <Blah />
            {props.children}
          </>
        </ThemeProvider>
      </MuiThemeProvider>
    </StyledEngineProvider>
  );
};

In fact, even within my GlobalStyle level, styled-components was able to return the proper theme object with my custom definitions, but not when using useTheme.

import { createGlobalStyle, css } from 'styled-components';
const GlobalStyle = createGlobalStyle`
  ${({ theme }: MyThemeOptions) => {
    console.debug('[GlobalStyle] theme?', theme); // <==== prints the proper theme object
    const globalStyle = css`
      ${require('../assets/styles/index.css')}
      ${generateRootCssVariables(theme)}
      ${TypographyStyle(theme)}
    `;
    // console.debug(globalStyle);
    return globalStyle;
  }}`;

Is this related?

`npx @mui/envinfo` ``` System: OS: Linux 5.13 Ubuntu 21.10 21.10 (Impish Indri) Binaries: Node: 14.19.2 - ~/.config/nvm/versions/node/v14.19.2/bin/node Yarn: Not Found npm: 6.14.17 - ~/.config/nvm/versions/node/v14.19.2/bin/npm Browsers: Chrome: 101.0.4951.64 Firefox: 100.0.2 npmPackages: @emotion/react: ^11.9.0 => 11.9.0 @emotion/styled: ^11.8.1 => 11.8.1 @mui/base: 5.0.0-alpha.82 @mui/material: ^5.8.1 => 5.8.1 @mui/private-theming: 5.8.0 @mui/styled-engine: 5.8.0 @mui/styled-engine-sc: ^5.8.0 => 5.8.0 @mui/system: 5.8.1 @mui/types: 7.1.3 @mui/utils: 5.8.0 @types/react: ^17.0.2 => 17.0.2 react: ^17.0.2 => 17.0.2 react-dom: ^17.0.2 => 17.0.2 styled-components: ^5.3.5 => 5.3.5 typescript: ^4.6.4 => 4.6.4 ```
siriwatknp commented 2 years ago

@peterzagoranski @totszwai I think it would be better if you can share the CodeSandbox so I can reproduce and provide you with the correct solution.

You can start from here https://material-ui.com/r/issue-template-latest.

totszwai commented 2 years ago

@siriwatknp I'm not sure how to reproduce a codesandbox with our specific use-case. We build a library with MyThemeProvider which wraps 2 of those theme providers from Mui/styled-components, then we use this library in our many other single-spa applications.

If I run and serve the the files within the same project of MyThemeProvider, then everything works... however using the exported MyThemeProvider in single-spa environment, despite passing the custom theme object it just won't propagate to the children.

In the following screen captures below, I dumped the custom theme object that I am passing over to MyThemeProvider, then as you could see immediately after, I use various useTheme hooks to try to access the theme object, however all of them returned the wrong theme object.

image image

This is my Test component that I created trying to troubleshoot what the heck is going on (and why is there so many useTheme hooks everywhere!)

import { useTheme as useThemeMuiMaterial } from '@mui/material';
import { useTheme as useThemeMuiSystem } from '@mui/system';
import { useTheme as useThemeMuiMaterialStyles } from '@mui/material/styles';
import { useTheme as useThemeSC } from 'styled-components';
import { useTheme as useThemeEmotion } from '@emotion/react';

export const Test = (props: unknown) => {
  console.debug('@mui/material', useThemeMuiMaterial());
  console.debug('@mui/system', useThemeMuiSystem());
  console.debug('@mui/material/styles', useThemeMuiMaterialStyles());
  console.debug('styled-components', useThemeSC());
  console.debug('@emotion/react', useThemeEmotion());

  return <div>SADNESS ENSURED</div>;
};

How I loaded it:

    <GvtThemeProvider theme={MyTheme}>
      <I18NextProvider i18n={i18n} loading={<Loading />}>
        <Test />
      </I18NextProvider>
    </GvtThemeProvider>

I mean I've created sandbox for simple project before, however I'm not sure how to create such scenario where the ThemeProvider gets exported and imported elsewhere.

siriwatknp commented 2 years ago

@totszwai Are you using styled-components and emotion at the same time?

totszwai commented 2 years ago

@siriwatknp no, we are only using styled-components.

With combination of styled-components and emotion (it seems that internally Mui favor emotion), there are so many hooks going around now...

Question for you,

siriwatknp commented 2 years ago

Which useTheme, createTheme and theme provider should we use for Mui v5.7 and v5.8? It is so incredibly confusing that we don't know what's right or what's wrong...

Always use them from @mui/material/styles (it is the same as @mui/material).

What kind of theme provider setup do we need with Mui v5.8 now? Does having both styled-components + mui/material theme providers still work/apply?

In general, you don't need the ThemeProvider from styled-components. Have you checked out the doc about using Material UI with styled-components yet?

Currently, Mui cannot really function without emotion, so when we build and package the library, we are bundling the emotion stuff despite we don't use it... what is the recommended bundling strategy when using Mui? Because we are getting the following warnings from emotion too:

Honestly, I would love to help but without any tangible code that I can reproduce, I am really lost. This kind of issue is quite hard to debug and mostly comes from the project setup. It might be the framework you are using, the webpack config, the package resolution, ...etc. So I suggest you should set up a minimal repo that is reproducible and then we will look into it.

Sakura-pgh commented 2 years ago

@totszwai I encountered the same problem as you in the ThemeProvider exporting mui using single-spa applications, have you solved it?

totszwai commented 2 years ago

@totszwai I encountered the same problem as you in the ThemeProvider exporting mui using single-spa applications, have you solved it?

Sadly nope. IMHO, styled-components and emotion should deal with existing instance gracefully. Like whenever it tries to initialize it should just check and see if there's already an instance running...

jpmariano commented 1 year ago

In React typescript, you have to declare that the variable is a type of ThemeOptions.

` import * as React from 'react'; import { ThemeOptions } from '@mui/material';

const theme:ThemeOptions = useTheme(); const theme:ThemeOptions = React.useContext(ThemeContext); `

warmbowski commented 1 year ago

@totszwai We have the same problem. useTheme always returns the default theme and not our custom theme when used in in our main app. Like you, we have also broken our theme provider out into a package and import it into our main app. The context looks to be loading the proper custom theme in the main app, but useTheme always returns the default theme. We first noticed this because whenever we used the Box component theme (in the sx prop or on the color props), it would also always use the default theme instead of the custom theme, and it looks like that component uses useTheme under the hood. This ONLY affected the Box component theme. Other MUI components we used had no problem consuming the custom theme from the context.

yagopv commented 1 year ago

Same is happening to us. We are developing a component library for our applications and we are not able to access the theme from the components in the library. All is working fine for the components in the main application.

We end exporting a ThemeProvider from the library and double wrapping it as you can see in the README instructions. This way we get all the components to work.

Using only the exported ThemeProvider don't work either as in that case the main app components cannot access the theme.

warmbowski commented 1 year ago

@yagopv can you elaborate on how you compile your libraries vs your applications? In our case, all of our libraries are compiled using tsc (typescript compiler) while our main apps are using CRA's default webpack/babel setup (for typescript). So the libs get transpiled by tsc as esmodules, but then imported into the main apps and probably end up transpiling again using babel setup of CRA. The result is the ThemeProvider driven MUI components (e.g. Box, useThem, etc) pick up our custom theme fine when imported into the component library lib, then exported for use in the main app. But the same MUI components don't pick up the custom theme when imported directly into the app from the mui packages. I surmise that this might have something to do with our issue (not completely sure yet).

jorgegarba commented 1 year ago

Having the same problem using custom themeProvider.

Here my files.

customTheme.tsx

import { COLOR_PRIMARY, COLOR_SECONDARY } from '@/constants';
import { createTheme } from '@mui/material';

export const angelesTheme = createTheme({
  palette: {
    primary: {
      main: COLOR_PRIMARY,
    },
    secondary: {
      main: COLOR_SECONDARY,
    },
  },
});

_app.tsx

import '@/styles/globals.css';
import { angelesTheme } from '@/theme/angelesTheme';
import { ThemeProvider } from '@emotion/react';
import type { AppProps } from 'next/app';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import 'animate.css';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider theme={angelesTheme}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

component.tsx


import {
  ...
  ...
  useTheme,
} from '@mui/material';
...
const FinancingListItem = styled(ListItem)<{
  isSelected?: boolean;
  color: 'primary' | 'secondary';
}>(({ isSelected = false, color, theme: { palette } }) => {
  console.log('palette', palette);

  return {
    border: isSelected ? `1px solid ${palette.primary.main}` : 'none',
    borderRadius: '5px',
    backgroundColor:
      color === 'primary'
        ? alpha(palette.primary.main, 0.9)
        : alpha(palette.secondary.main, 0.9),
    color:
      color === 'primary'
        ? palette.primary.contrastText
        : palette.secondary.contrastText, // This shows me the correct color of the custom secondary color ✅ 
  };
});
...

const Step2Financing = () => {
  const theme = useTheme();
  console.log(theme.palette.secondary.main); // This is showing me the deafult color palette 💀 

...

Thank you for your attention. @siriwatknp

michaeltford commented 1 year ago

I was getting this issue and I was able to 'resolve' it.

I was getting the problem because there were 2 instances of mui (I have a mono repo and while versions only differed by a single patch 5.11.13 vs 5.11.12 if created a very hard to find bug).

I still believe this is an issue with a potential 'workaround within' mui. (This creates very hard to find and fragile code as only some components started adopting the defaulttheme)

The build was not transpiling the ThemeProviders in the same order so react.useContext was return null for one instance and a valid value for another instance of identical code from 2 different packages.

I don't understand the code base but perhaps the @mui team can review the need for 2 ThemeProviders and useTheme packages.

import ThemeContext from '@mui/private-theming/useTheme/ThemeContext'; import { ThemeContext } from "@mui/material/styles";

If there is a reason for 2 implementations perhaps the default useTheme should try to resolve both provider contexts before returning the default.

Hanz-Valta commented 1 year ago

I'am struggling to fix it in my pnpm Turborepo with multiple NextJs apps with shared UI package, useTheme inside UI package always returns default material theme and not the custom one thats being applied in the app. Any updates on this ?

My monorepo structure:

apps | - app1 | - app2 (useTheme returns my theme) packages | - ui (useTheme or components from MUI use the default material theme when used inside app2)

I use the same MUI version(5.14.7) everywhere in this Turborepo starter

dmytro-shchurov commented 5 months ago

It's still there in 5.15.x When an app uses an import from mui/material and an npm module uses import from mui/material/styles (referencing the same mui package with peerDependencies), these are two different instances. Vite is a bundler.

Then next.. If a same import from mui/material/styles is used, and I create a module local theme from a settings file, my local changes are ignored despite useTheme returns them. So, useTheme sees a local theme, but MUI components (charts in particular) ignore it. When I change an external, application main theme, the components in the module are fine. When I define my custom component style rules with slots with using styled() everything is fine again.

Please, remove an export of these related objects/hooks from mui/material (keep them only in mui/material/styles, cause there is no reason to have these two exports, and your own components could use the different imports, and we won't get an idea which one uses what), and it would be interesting why a h.. mui charts (and not only) ignore a local theme which is visible with useTheme... I see style tags in a result html with custom styled() rules defined in the module, but there are no any changes to style tags when I put a new local theme (a main external theme passed as a module component property is merged with the local settings) into a ThemeProvider in the module.

dmitriy-chuchuva commented 5 months ago

I had a similar issue where I upgraded in a ts app, @mui/material from 5.2.4 to 5.15.14 which caused some jest tests to break. The test no longer applied the theme we defined in the test but defaulted to the default MUI theme. After reading this thread, I changed the imports from

import { ThemeProvider } from '@mui/styles';
import { createTheme } from '@mui/material';

to import { createTheme, ThemeProvider } from '@mui/material/styles';

This fixed my issue.