NextJS: Implementing Dynamic MUI Themes for Subdomains

In this article, I will be using NextJS and MUI to create a project that has the ability to switch themes depending on the subdomain. When a user visits a different subdomain, the theme name will be fetched from the database and the server will build the corresponding page. To accomplish this, I will be using the getInitialProps method to initialize the page on the server. Although getServerSideProps can also be used, it cannot be used with the current App configuration and therefore must be executed on each page. Additionally, I will be utilizing the useContext hook to share the fetched data across other pages.

styles/mui/theme.js

import { createTheme } from "@mui/material/styles";
export const theme1 = createTheme({
  palette: {
   mode: "light",
   common: {
     black: "#000",
     white: "#fff",
   },
   primary: {
     main: "#3E4CA0",
     light: "#3E4CA0",
     dark: "#3E4CA0",
     contrastText: "#fff",
   },
   secondary: {
     main: "#252A36",
     light: "#252A36",
     dark: "#252A36",
     contrastText: "#fff",
   },
 },
});
export const theme2 = createTheme({
  palette: {
   mode: "light",
   common: {
     black: "#000",
     white: "#fff",
   },
   primary: {
     main: "#D4622B",
     light: "#D4622B",
     dark: "#D4622B",
     contrastText: "#fff",
   },
   secondary: {
     main: "#181818",
     light: "#181818",
     dark: "#181818",
     contrastText: "#fff",
   },
 },
});
export const theme3 = createTheme({
  palette: {
   mode: "light",
   common: {
     black: "#000",
     white: "#fff",
   },
   primary: {
     main: "#141529",
     light: "#141529",
     dark: "#141529",
     contrastText: "#fff",
   },
   secondary: {
     main: "#686DB1",
     light: "#686DB1",
     dark: "#686DB1",
     contrastText: "#fff",
   },
 },
});
export const theme4 = createTheme({
  palette: {
   mode: "light",
   common: {
     black: "#000",
     white: "#fff",
   },
   primary: {
     main: "#FCC135",
     light: "#FCC135",
     dark: "#FCC135",
     contrastText: "#fff",
   },
   secondary: {
     main: "#063E5F",
     light: "#063E5F",
     dark: "#063E5F",
     contrastText: "#fff",
   },
 },
});

/src/productContext.js

import React from “react”;
const ProductContext = React.createContext();
export default ProductContext;

/pages/_app.js

import App from "next/app";
import absoluteUrl from "next-absolute-url";
import { ThemeProvider } from "@mui/material/styles";
import ProductContext from "../src/productContext";
// ==== IMPORT THEMES ====
import { theme1, theme2, theme3, theme4 } from "../styles/mui/theme";
import "../styles/globals.css";
MyApp.getInitialProps = async (appContext) => {
  const { protocol, host } = absoluteUrl(appContext.ctx.req, "vcap.me:3000");
  const sub_domain = host.split(".", 1);
  const store_name = sub_domain[0];
  const res = await fetch(
   "https://dummyjson.com/products/" + store_name
  );
  const productDetails = await res.json();
  console.log(productDetails);
  return { props: { productDetails } };
};
function MyApp({ Component, pageProps, props }) {
  let selectedTheme = theme1;
  if (props.productDetails) {
    if (props.productDetails.brand === "Apple") {
      selectedTheme = theme1;
    } else if (props.productDetails.brand === "Samsung") {
      selectedTheme = theme2;
    } else if (props.productDetails.brand === "OPPO") {
      selectedTheme = theme3;
    } else if (props.productDetails.brand === "Huawei") {
      selectedTheme = theme4;
  }
}
return (
  <ThemeProvider theme={selectedTheme}>
    <ProductContext.Provider value={props.productDetails}>
      <Component {...pageProps} />
    </ProductContext.Provider>
  </ThemeProvider>
  );
}
export default MyApp;

/pages/index.js

import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import ProductContext from "../src/productContext";
import { useContext } from "react";
import { AppBar, Typography, Toolbar, Button } from "@mui/material";
export default function Home() {
  const product = useContext(ProductContext);
  return (
    <div className={styles.container}>
      <Head>
        <title>{product.title}</title>
      </Head>
      <AppBar>
        <Toolbar>
          <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>News</Typography>
          <Button color="inherit">Login</Button>
        </Toolbar>
      </AppBar>
      <main className={styles.main}>
        <Typography variant="h2" color="primary">
           Welcome to {product.brand}
        </Typography>
        <Typography variant="h4" color="secondary">
          {product.title}
        </Typography>
        <Typography variant="body1" color="secondary">
          {product.description}
        </Typography>
        <img src={product.images[0]} />
       </main>
     </div>
  );
}

output:

http://1.vcap.me:3000 or http://1.lacolhost.com:3000

http://2.lacolhost.com:3000