GLM 4.5 Air

Fix Custom MUI Palette Not Applied in React/TypeScript

Troubleshoot why your custom Material UI palette colors aren't applying in React/TypeScript projects. Learn the correct MUI v5 theme configuration to fix palette issues.

Question

Custom MUI Palette Theme Not Being Applied in React/TypeScript Project

Problem Description

I’m working on a React/TypeScript project using Material UI v5 and Vite. I’ve created a custom palette theme, but the custom colors are not being applied to my components. The theme appears to be loading correctly, but the palette colors are defaulting to Material UI’s standard colors instead of my custom ones.

Project Setup

  • Stack: TypeScript, React, Material UI v5, Vite
  • The project is set up to bundle multiple websites into one hosting service

File Structure

  1. App.tsx: Main page component
  2. global_theme.tsx: Contains light/dark mode configuration, CSS variables, and custom breakpoint values
  3. theme.tsx: Contains the actual palette variables I’m trying to use

Code Snippets

App.tsx

tsx
import Typography from '@mui/material/Typography';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import { Box, CssBaseline, Link } from '@mui/material';

import { ThemeProvider } from '@mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from '@emotion/react';
import { StyledEngineProvider } from "@mui/material";

import './App.css'
import homepageTheme from './theme';

function App() {
    const homeTheme = homepageTheme;
    console.log(homeTheme);

    return (
        <div>
            <CssBaseline />
            <ThemeProvider theme={homeTheme}>
            <Emotion10ThemeProvider theme={homeTheme}>
            <StyledEngineProvider>
                <Box id="homepage" sx={{ backgroundColor: homeTheme.palette.background.default }}>
                    <Card sx={{ p: 5, mb: 10, backgroundColor: homeTheme.palette.primary.light }}>
                        <CardContent>
                            <Typography id="page-title" sx={{ fontSize: 52, color: homeTheme.palette.primary.contrastText }}>
                                Zach M
                            </Typography>
                        </CardContent>
                    </Card>
                </Box>
            </StyledEngineProvider>
            </Emotion10ThemeProvider>
            </ThemeProvider>
        </div>
    )
}

export default App

global_theme.tsx

tsx
import { createTheme, type ThemeOptions } from "@mui/material/styles";

declare module '@mui/material/styles' {
  interface BreakpointOverrides {
    mobile: true; // adds custom breakpoints
    tablet: true;
    laptop: true;
    desktop: true;
    xs: false; // removes the default breakpoints
    sm: false;
    md: false;
    lg: false;
    xl: false;
  }
}

const globalTheme = createTheme({
    breakpoints: {
        values: {
            mobile: 0,
            tablet: 640,
            laptop: 1024,
            desktop: 1200,
        },
    },
    colorSchemes: {
        light: true,
        dark: true,
    },
    cssVariables: true,
});

export default globalTheme;

theme.tsx

tsx
import { createTheme } from '@mui/material/styles';
import { grey, blue, indigo, cyan, red, purple } from '@mui/material/colors';
import globalTheme from './global_theme';

declare module '@mui/material/styles' {
    interface Palette {
        custom: Palette['primary'];
    }
    interface PaletteOptions {
        custom?: PaletteOptions['primary'];
    }
}

const homepageTheme = createTheme({
    ...globalTheme,
    colorSchemes: {
        light: {
            palette: {
                background: {
                    default: purple[100],
                },
                primary: {
                    main: '#4d858aff',
                    dark: '#203840ff',
                    light: red[500],
                    contrastText: '#0b1d23ff',
                },
                secondary: {
                    main: '#c3e8ed',
                    light: cyan[50],
                    dark: '#afd6db',
                    contrastText: grey[800],
                }
            },
        },
        dark: {
            palette: {
                background: {
                    default: blue[900]
                },
                primary: {
                    main: blue[900],
                    contrastText: blue[200],
                },
                secondary: {
                    main: indigo[500],
                    /*contrastText: indigo[100],*/
                }
            },
        },
    },
});

export default homepageTheme;

Console Output

When I log the theme object, I can see that the breakpoints are correctly configured, but the custom palette colors are not being applied. The console output shows the default MUI colors instead of my custom ones.

What I’ve Tried

  • Checked for package updates (npm)
  • Verified all imports are correct
  • Confirmed that theme.tsx is using createTheme() correctly
  • Verified that I’m accessing the palette variables correctly
  • Set <CssBaseline />
  • Set <ThemeProvider> and <StyledEngineProvider>
  • Tried both bgcolor and backgroundColor
  • Tried both sx={{}} and style=

Question

Why is my custom MUI palette theme not being applied to my components, and how can I fix this issue?

Additional Context

I’ve reviewed the Material UI documentation and several StackOverflow posts, but I haven’t been able to resolve this issue. My current theory is that the problem might be related to how I’m nesting theme imports or splitting the theme between files, but I’m not sure why that would cause the issue.

GLM 4.5 Air

Custom MUI Palette Theme Not Being Applied in React/TypeScript Project

The issue with your custom MUI palette not being applied is likely related to how you’re structuring your theme configuration and extending the global theme. Let’s break down the problem and provide a solution.

Brief Answer

Your custom MUI palette isn’t being applied because of incorrect theme extension in theme.tsx and potential conflicts with multiple theme providers. In Material UI v5, you need to properly extend the base theme and ensure the theme configuration follows the v5 syntax, particularly for light/dark mode handling.

Contents

Common Theme Configuration Issues in MUI v5

Material UI v5 introduced significant changes to how themes are configured, especially for light/dark modes. Based on your code, here are the main issues:

  1. Incorrect Theme Extension: You’re using ...globalTheme but not properly extending the theme structure.
  2. Outdated Color Schemes Syntax: The colorSchemes property is not the correct way to define light/dark themes in MUI v5.
  3. Palette Structure: Your palette configuration doesn’t properly extend the base palette.
  4. Multiple Theme Providers: Nesting multiple theme providers can cause conflicts.

Fixing the Theme Structure

Let’s fix your theme.tsx file to properly extend the global theme:

tsx
import { createTheme, Theme } from '@mui/material/styles';
import { grey, blue, indigo, cyan, red, purple } from '@mui/material/colors';
import globalTheme from './global_theme';

// Type declarations for custom palette
declare module '@mui/material/styles' {
    interface Palette {
        custom: Palette['primary'];
    }
    interface PaletteOptions {
        custom?: PaletteOptions['primary'];
    }
}

const homepageTheme = (mode: 'light' | 'dark' = 'light'): Theme => {
    const baseTheme = createTheme(globalTheme);
    
    return createTheme({
        ...baseTheme,
        palette: {
            ...baseTheme.palette,
            mode: mode,
            ...(mode === 'light' ? {
                primary: {
                    main: '#4d858aff',
                    dark: '#203840ff',
                    light: red[500],
                    contrastText: '#0b1d23ff',
                },
                secondary: {
                    main: '#c3e8ed',
                    light: cyan[50],
                    dark: '#afd6db',
                    contrastText: grey[800],
                },
                background: {
                    default: purple[100],
                },
            } : {
                primary: {
                    main: blue[900],
                    contrastText: blue[200],
                },
                secondary: {
                    main: indigo[500],
                    contrastText: indigo[100],
                },
                background: {
                    default: blue[900],
                },
            }),
            // Add custom palette if needed
            custom: {
                main: '#ff0000', // Example custom color
                light: '#ff6666',
                dark: '#cc0000',
                contrastText: '#ffffff',
            },
        },
    });
};

export default homepageTheme;

Proper Theme Provider Setup

In your App.tsx, you’re nesting multiple theme providers, which can cause conflicts. Here’s the correct approach:

tsx
import Typography from '@mui/material/Typography';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import { Box, CssBaseline } from '@mui/material';
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from '@emotion/react';

import './App.css';
import homepageTheme from './theme';

function App() {
    // Create theme with light mode (you can change to 'dark' if needed)
    const homeTheme = homepageTheme('light');
    console.log(homeTheme);

    return (
        <StyledEngineProvider injectFirst>
            <ThemeProvider theme={homeTheme}>
                <Emotion10ThemeProvider theme={homeTheme}>
                    <CssBaseline />
                    <Box id="homepage" sx={{ backgroundColor: 'background.default', minHeight: '100vh' }}>
                        <Card sx={{ p: 5, mb: 10, backgroundColor: 'primary.light' }}>
                            <CardContent>
                                <Typography 
                                    id="page-title" 
                                    sx={{ 
                                        fontSize: 52, 
                                        color: 'primary.contrastText',
                                        fontWeight: 'bold'
                                    }}
                                >
                                    Zach M
                                </Typography>
                            </CardContent>
                        </Card>
                    </Box>
                </Emotion10ThemeProvider>
            </ThemeProvider>
        </StyledEngineProvider>
    );
}

export default App;

Verifying Theme Application

To ensure your theme is being applied correctly:

  1. Check the Theme Object: Log the theme object and verify that your custom colors exist:

    tsx
    console.log('Primary light:', homeTheme.palette.primary.light);
    console.log('Background default:', homeTheme.palette.background.default);
    
  2. Use Theme Tokens: Instead of hardcoded color values, use theme tokens:

    tsx
    // Instead of: backgroundColor: homeTheme.palette.primary.light
    // Use: backgroundColor: 'primary.light'
    
  3. Inspect the DOM: Use browser dev tools to check if CSS variables are being applied to your elements.

  4. Test with Simple Components: Create a simple test component to verify the theme:

    tsx
    import { Button, Paper } from '@mui/material';
    
    function TestComponent() {
        return (
            <div>
                <Button variant="contained" color="primary">
                  Primary Button
                </Button>
                <Paper sx={{ mt: 2, p: 2, bgcolor: 'background.default' }}>
                  Test Paper with custom background
                </Paper>
            </div>
        );
    }
    

Complete Working Example

Here’s a simplified, working version of your theme configuration:

global_theme.tsx

tsx
import { createTheme, type ThemeOptions } from "@mui/material/styles";

declare module '@mui/material/styles' {
  interface BreakpointOverrides {
    mobile: true;
    tablet: true;
    laptop: true;
    desktop: true;
    xs: false;
    sm: false;
    md: false;
    lg: false;
    xl: false;
  }
}

const globalTheme = createTheme({
    breakpoints: {
        values: {
            mobile: 0,
            tablet: 640,
            laptop: 1024,
            desktop: 1200,
        },
    },
});

export default globalTheme;

theme.tsx

tsx
import { createTheme } from '@mui/material/styles';
import { grey, blue, indigo, cyan, red, purple } from '@mui/material/colors';
import globalTheme from './global_theme';

declare module '@mui/material/styles' {
    interface Palette {
        custom: Palette['primary'];
    }
    interface PaletteOptions {
        custom?: PaletteOptions['primary'];
    }
}

const homepageTheme = (mode: 'light' | 'dark' = 'light') => {
    const baseTheme = createTheme(globalTheme);
    
    return createTheme({
        ...baseTheme,
        palette: {
            mode: mode,
            primary: {
                main: '#4d858aff',
                dark: '#203840ff',
                light: red[500],
                contrastText: '#0b1d23ff',
            },
            secondary: {
                main: '#c3e8ed',
                light: cyan[50],
                dark: '#afd6db',
                contrastText: grey[800],
            },
            background: {
                default: mode === 'light' ? purple[100] : blue[900],
            },
            custom: {
                main: '#ff0000',
                light: '#ff6666',
                dark: '#cc0000',
                contrastText: '#ffffff',
            },
        },
    });
};

export default homepageTheme;

App.tsx

tsx
import { Box, CssBaseline, Typography, Card, CardContent } from '@mui/material';
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from '@emotion/react';

import './App.css';
import homepageTheme from './theme';

function App() {
    const homeTheme = homepageTheme('light'); // or 'dark'

    return (
        <StyledEngineProvider injectFirst>
            <ThemeProvider theme={homeTheme}>
                <Emotion10ThemeProvider theme={homeTheme}>
                    <CssBaseline />
                    <Box id="homepage" sx={{ bgcolor: 'background.default', minHeight: '100vh' }}>
                        <Card sx={{ p: 5, mb: 10, bgcolor: 'primary.light' }}>
                            <CardContent>
                                <Typography 
                                    id="page-title" 
                                    sx={{ 
                                        fontSize: 52, 
                                        color: 'primary.contrastText',
                                        fontWeight: 'bold'
                                    }}
                                >
                                    Zach M
                                </Typography>
                            </CardContent>
                        </Card>
                    </Box>
                </Emotion10ThemeProvider>
            </ThemeProvider>
        </StyledEngineProvider>
    );
}

export default App;

Conclusion

The key issues with your custom MUI palette not being applied were:

  1. Incorrect Theme Extension: The theme wasn’t properly extending the global theme
  2. Outdated Color Schemes Syntax: Using colorSchemes instead of the v5 palette.mode approach
  3. Multiple Theme Providers: Nesting providers can cause conflicts

By fixing these issues and following the Material UI v5 theme structure, your custom palette colors should now be applied correctly to your components. Remember to use theme tokens (like 'primary.light') instead of accessing theme properties directly in your JSX for better maintainability.