GLM 4.5 Air

Fix Material UI Custom Palette Theme Not Applied: Complete Guide

Troubleshoot Material UI v5 custom palette theme issues in React TypeScript. Learn why your colors aren't applying and fix theme configuration problems with step-by-step solutions.

Question

Custom Material UI Palette Theme Not Being Applied in React with TypeScript

Technical Stack

  • TypeScript
  • React
  • Material UI v5
  • Vite

Problem Description

I’m trying to apply a custom palette theme in my Material UI application, but the custom colors are not being applied. Despite following the documentation and checking various resources, I can’t identify what’s wrong with my implementation.

File Structure

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

Code Implementation

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;
    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,
        },
    },
    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],
                }
            },
        },
    },
});

export default homepageTheme;

Console Output

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

Troubleshooting Steps Already Taken

  • Checked for package updates (npm)
  • Verified all imports are correct
  • Confirmed that theme.tsx is using createTheme() correctly
  • Verified that palette variables are being accessed correctly
  • Added <CssBaseline /> component
  • Implemented <ThemeProvider> and <StyledEngineProvider>
  • Tried both bgcolor and backgroundColor properties
  • Tested both sx={{}} and style= attributes

Expected Behavior

The custom colors defined in the theme should be applied to the components. For example:

  • The background should be purple[100] (light purple)
  • The card background should be red[500] (red)
  • The text should be #0b1d23ff (dark blue)

Actual Behavior

The default Material UI colors are being applied instead of the custom colors.

Additional Information

The project was set up using Vite, which resolved package management issues, but I’m wondering if this might be causing the theme configuration problem.

I’ve reviewed multiple StackOverflow posts and the official Material UI documentation, but haven’t been able to resolve this issue. The code compiles without errors or warnings.

GLM 4.5 Air

Custom Material UI Palette Theme Not Being Applied in React with TypeScript

Brief Answer: Your custom Material UI palette theme isn’t being applied due to incorrect theme extension structure and potential conflicts between your global and custom theme configurations. The main issues are in how you’re extending the base theme and how you’re structuring the color schemes in Material UI v5.

Contents


Understanding the Theme Extension Problem

The core issue in your implementation is how you’re extending themes in Material UI v5. Material UI v5 introduced significant changes to theming, particularly with the new color scheme API. When you create a theme in global_theme.tsx and then try to extend it in theme.tsx, you’re not properly merging the configurations.

In Material UI v5, themes should be created using the createTheme() function with proper extension syntax. Simply spreading the global theme isn’t enough when working with color schemes.

The console output shows that your breakpoints are correctly configured but the custom colors aren’t applied, which indicates that while the theme structure is partially working, the palette configuration isn’t being properly merged or applied.


Fixing the Global Theme Configuration

Your global_theme.tsx needs to be restructured to properly define theme options rather than a complete theme:

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;
  }
}

export const globalThemeOptions: ThemeOptions = {
    breakpoints: {
        values: {
            mobile: 0,
            tablet: 640,
            laptop: 1024,
            desktop: 1200,
        },
    },
    colorSchemes: {
        light: true,
        dark: true,
    },
    cssVariables: true,
};

// Create a base theme that can be extended
export const baseTheme = createTheme(globalThemeOptions);

This approach separates theme options from the complete theme, making it easier to extend and modify in other files.


Correcting the Custom Theme Implementation

Your theme.tsx file needs to properly extend the base theme using the correct syntax for Material UI v5’s color scheme API:

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

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

const homepageTheme = createTheme({
    ...baseTheme,
    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],
                },
                custom: {
                    main: '#ff0000',
                    light: '#ff6666',
                    dark: '#cc0000',
                    contrastText: '#ffffff',
                }
            },
        },
        dark: {
            palette: {
                background: {
                    default: blue[900]
                },
                primary: {
                    main: blue[900],
                    contrastText: blue[200],
                },
                secondary: {
                    main: indigo[500],
                },
                custom: {
                    main: '#00ff00',
                    light: '#66ff66',
                    dark: '#00cc00',
                    contrastText: '#000000',
                }
            },
        },
    },
});

export default homepageTheme;

Key changes:

  1. Properly extending baseTheme using the spread operator
  2. Correctly structuring the colorSchemes with both light and dark variants
  3. Adding the custom palette color to both schemes

Simplifying the Theme Provider Setup

Your App.tsx has multiple theme providers that could be causing conflicts. Simplify it like this:

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 } from '@mui/material/styles';
import { StyledEngineProvider } from "@mui/material";

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

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

export default App

Changes made:

  1. Removed the redundant Emotion10ThemeProvider as it’s not needed when using Material UI’s ThemeProvider
  2. Simplified the component references to use theme keys directly (e.g., 'background.default' instead of homeTheme.palette.background.default)
  3. Kept StyledEngineProvider with injectFirst which is important for proper styling precedence

Alternative Approaches for Theme Configuration

If the above approach doesn’t work, consider these alternatives:

1. Using Simple Palette Extension

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

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

const homepageTheme = createTheme({
    ...baseTheme,
    palette: {
        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],
        },
        custom: {
            main: '#ff0000',
            light: '#ff6666',
            dark: '#cc0000',
            contrastText: '#ffffff',
        },
    },
});

export default homepageTheme;

2. Using a Theme Factory

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

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

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

export default createHomepageTheme('light');
export const darkHomepageTheme = createHomepageTheme('dark');

Verifying the Theme Application

After implementing the fixes, you can verify that your theme is applied correctly:

  1. Check the console output: Modify your App.tsx to log specific palette values:
tsx
function App() {
    return (
        <ThemeProvider theme={homepageTheme}>
            <Box>
                {/* Your components */}
                {console.log('Primary main:', homepageTheme.palette.primary.main)}
                {console.log('Background default:', homepageTheme.palette.background.default)}
            </Box>
        </ThemeProvider>
    )
}
  1. Use browser dev tools: Inspect the CSS variables applied to the root element. With cssVariables: true enabled, you should see custom CSS variables like:

    --mui-palette-primary-main: #4d858aff;
    --mui-palette-background-default: #e1bee7;
    
  2. Test with direct styling: Add a temporary style to verify colors are working:

tsx
<Box sx={{ 
    backgroundColor: theme => theme.palette.primary.main,
    color: theme => theme.palette.primary.contrastText,
    p: 2 
}}>
    Test Box
</Box>

Conclusion and Best Practices

Key Takeaways:

  1. Material UI v5 uses a different theme extension approach than v4
  2. The colorSchemes API has a specific structure that must be followed
  3. Multiple theme providers can cause conflicts
  4. CSS variables are enabled by default when using cssVariables: true

Recommendations:

  1. Start simple: Begin with a basic theme and gradually add complexity
  2. Use TypeScript modules: Always declare palette customizations in module declarations
  3. Test incrementally: Verify each change to identify where things break
  4. Consider theme variants: Create separate light and dark themes rather than trying to merge them
  5. Use the dev tools: Material UI provides excellent dev tools for theme debugging

By following these guidelines, you should be able to successfully apply your custom palette theme to your Material UI application. Remember that Material UI v5’s theming system is powerful but requires careful attention to the new APIs and structure.

For further troubleshooting, consider using the Material UI Theme Creator tool to experiment with theme configurations before implementing them in your code.