Mike K
Mike K

Reputation: 6491

Extending Material UI theme via Module Augmentation not working correctly

I'm trying to extend Theme with Material UI, but it's throwing an error, saying that I'm not extending it correctly, saying,

Property 'layout' is missing in type 'Palette' but required in type 'IPaletteOptions'.

Here is what I have:

// src/theme/theme.d.ts
import { Theme } from "@material-ui/core/styles";
import {
  IPaletteOptions,
  PaletteOptions
} from "@material-ui/core/styles/createPalette";

type TLayout = {
  primary: string;
  secondary: string;
};

declare module "@material-ui/core/styles/createPalette" {
  export interface IPaletteOptions extends PaletteOptions {
    layout: TLayout;
  }
}

declare module "@material-ui/core/styles" {
  export interface ITheme extends Theme {
    palette: IPaletteOptions;
  }
}
// src/theme/index.ts
import { createMuiTheme, ITheme } from "@material-ui/core/styles";
import { IPaletteOptions } from "@material-ui/core/styles/createPalette";

export const palette = {
  layout: {
    primary: "#ffffff",
    secondary: "#e4e4e4"
  }
} as IPaletteOptions;

export default createMuiTheme({ palette }) as ITheme;
// src/App.tsx
import React from "react";
import { ThemeProvider, ITheme } from "@material-ui/core";
import theme from "./theme";

import Component from "./Component";

export default function App() {
  return (
    <ThemeProvider<ITheme> theme={theme}>
      <Component />
    </ThemeProvider>
  );
}
import { useTheme } from "@material-ui/core";
import React from "react";

export default React.memo(() => {
  const theme = useTheme(); // <-- this should be of type `ITheme` automatically
                            // If I do useTheme<ITheme>(), it shows property 'layout', but
                            // I shouldn't have to do that, because of the type casting that
                            // I do in App.tsx, where I import and use 'theme' from src/theme/index.ts

  return <div>the layout primary color is {theme.palette.layout.primary}</div>;
});

What am I doing wrong here? I would like to use ITheme throughout my app, which would extend the basic Theme in addition to what I add.

cool-matsumoto-4621h

Upvotes: 8

Views: 14847

Answers (3)

Hame
Hame

Reputation: 544

In Material v5 you can extend CommonColors

declare module "@mui/material/styles/createPalette" {
  interface CommonColors {
    layout: {
      offBlack: string;
      offWhite: string;
    }
  }
}

const colors = createTheme({
  palette: {
    common: {
      layout: {
        offBlack: "#14142B",
        offWhite: "#FCFCFC",
      },
    },
  },
});

// In a component
const useStyles = makeStyles((theme) => ({
  target: {
    backgroundColor: theme.palette.common.layout.offBlack
  }
}));

Upvotes: 8

nevace
nevace

Reputation: 817

I got this working by using this:

declare module '@material-ui/styles' {
  export interface DefaultTheme extends CustomTheme {}
}

Notice 'core' is not included in the module.

My full code:

import theme from './theme';

type CustomTheme = {
  palette: typeof theme;
};

declare module '@material-ui/styles' {
  export interface DefaultTheme extends CustomTheme {}
}

Upvotes: 0

tmhao2005
tmhao2005

Reputation: 17504

I'm thinking of it would be even handy to override things which uses your ITheme like as following:

theme.d.ts

declare module "@material-ui/core/styles" {
  export interface ITheme extends Theme {
    palette: IPaletteOptions;
  }
  
  // Keep overriding these things in your app
  // In this case returns `ITheme`
  export function createMuiTheme(options?: ThemeOptions, ...args: object[]): ITheme;
  
  // returns `ITheme`
  export function useTheme<T = ITheme>(): T;
}

Then you no longer need to cast returned type anymore in your theme/index.ts:

export default createMuiTheme({ palette });

In case of using useTheme, it's supposed to return ITheme as expected.

NOTE: I've also updated the codesandbox as well: https://codesandbox.io/s/charming-pasteur-s6h8v?file=/src/Component.tsx

Upvotes: 6

Related Questions