'Theme nesting with Material UI

I have many datatables throughout my website and for the most part they are all styled the same. There are several different styles I need to apply to some of them. I want to create a global theme for handling everything across the site including the basic datatable styles and I also want to have a local theme to tweak the individual datatables a little. Here is what I've got.

https://codesandbox.io/embed/jolly-antonelli-fg1y1

This is structure like this

    <Test>
        <PrimaryThemeHere> //All have Border 1px red
            <TestChild>
                <SecondaryThemeHere> //blue background
                    <Datatable />
                </SecondaryThemeHere>
            </TestChild>
            <TestChild2>
                <SecondaryThemeHere> //Red background
                    <Datatable />
                </SecondaryThemeHere>
            <TestChild2>
        </PrimaryThemeHere>
    </Test>

The primary theme looks like this:

const theme = createMuiTheme({
  overrides: {
    MuiTableBody: {
      root: {
        border: "1px solid red"
      }
    },
    MuiTableCell: {
      root: {
        border: "1px solid red"
      }
    }
  }
});

and the nested theme looks like this:

  getMuiTheme = () =>
    createMuiTheme({
      overrides: {
        MuiTableRow: {
          root: {
            backgroundColor: "blue"
          }
        }
      }
    });

I can never get the border red to show alongside the background color. It always chooses one or the other. How can I get a combination of the initial primary theming (border 1px red) and the background color or blue and red.

Please help



Solution 1:[1]

Here's the relevant portion of the documentation:

The code that handles theme nesting can be found here:

Here is the current code:

// To support composition of theme.
function mergeOuterLocalTheme(outerTheme, localTheme) {
  if (typeof localTheme === 'function') {
    const mergedTheme = localTheme(outerTheme);

    warning(
      mergedTheme,
      [
        'Material-UI: you should return an object from your theme function, i.e.',
        '<ThemeProvider theme={() => ({})} />',
      ].join('\n'),
    );

    return mergedTheme;
  }

  return { ...outerTheme, ...localTheme };
}

Notice that the final line (return { ...outerTheme, ...localTheme };) is doing a shallow merge of the two themes. Since both of your themes have the overrides property specified, the localTheme overrides will completely replace the outerTheme overrides.

However, you can do a more sophisticated merge of the two themes, by providing a function to the ThemeProvider. For instance TestChild can look like this:

import React, { Component } from "react";
import { MuiThemeProvider } from "@material-ui/core/styles";

import MUIDataTable from "mui-datatables";

const localTheme = {
  overrides: {
    MuiTableRow: {
      root: {
        backgroundColor: "blue"
      }
    }
  }
};
const themeMerge = outerTheme => {
  // Shallow copy of outerTheme
  const newTheme = { ...outerTheme };
  if (!newTheme.overrides) {
    newTheme.overrides = localTheme.overrides;
  } else {
    // Merge the overrides. If you have the same overrides key
    // in both (e.g. MuiTableRow), then this would need to be
    // more sophisticated and you would probably want to use
    // a deepMerge function from some other package to handle this step.
    newTheme.overrides = { ...newTheme.overrides, ...localTheme.overrides };
  }
  return newTheme;
};
class TestChild extends Component {
  render() {
    const columns = [
      {
        name: "Message"
      },
      {
        name: "Date"
      },
      {
        name: "Dismiss"
      }
    ];
    const data = [["test", "15/01/19", "", ""], ["test", "15/01/19", "", ""]];
    let options = {
      filterType: "dropdown",
      responsive: "stacked",
      print: false,
      search: false,
      download: false,
      selectableRows: "none"
    };

    return (
      <div>
        <MuiThemeProvider theme={themeMerge}>
          <MUIDataTable
            title={"Test"}
            data={data}
            columns={columns}
            options={options}
          />
        </MuiThemeProvider>
      </div>
    );
  }
}

export default TestChild;

Edit Merge theme overrides

In my version of your sandbox, I only fixed TestChild2.js.

Solution 2:[2]

For me the whole inner theme worked, except the mode. I could fix it by adding a <Paper /> component.

import { createTheme, Paper, ThemeProvider } from "@mui/material";

const outerThemeOptions = {
  palette: { mode: "light" },
  typography: { body1: { fontSize: 14 } },
};

const innerThemeOptions = {
  palette: { mode: "dark" },
};

const outerTheme = createTheme(outerThemeOptions);
const innerTheme = createTheme({
  ...outerThemeOptions,
  ...innerThemeOptions,
});

<ThemeProvider theme={outerTheme}>
  <Child1 />
  <ThemeProvider theme={innerTheme}>
    <Paper elevation={0}>
      <Child2 />
    </Paper>
  </ThemeProvider>
</ThemeProvider>;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ryan Cogswell
Solution 2