How to change the font family in Next JS when used with Material UI?

Viewed 4350

I am recently getting started with Next JS. for styling, I am using Material UI. one issue I am facing is with the fonts. I couldn't able to change the font family to a different font. as per the below example (Github link), I created a _document.js page inside my pages folder

https://github.com/mui-org/material-ui/tree/master/examples/nextjs/pages

_document.js in the below code I tried changing Roboto with Quicksand

import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";
// import theme from "../components/Theme.js";

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          {/* PWA primary color */}
          {/* <meta name="theme-color" content={theme.palette.primary.main} /> */}
          <link
            rel="stylesheet"
            // href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
            href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700&display=swap"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
 
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,
    // Styles fragment is rendered after the app and page rendering finish.
    styles: [
      ...React.Children.toArray(initialProps.styles),
      sheets.getStyleElement(),
    ],
  };
};

Also, I customized my MUI theme object as below

const theme = createMuiTheme({
  palette: {
    type: "dark",
    background: {
      default: "#212121",
    },
    typography: {
      fontFamily: "Quicksand",
    },
  },
});
   

Layout.js

import { createMuiTheme } from "@material-ui/core/styles";
import { ThemeProvider } from "@material-ui/styles";
import { makeStyles } from "@material-ui/core/styles";
import CssBaseline from "@material-ui/core/CssBaseline";
import Container from "@material-ui/core/Container";
import Divider from "@material-ui/core/Divider";
import NavBar from "./NavBar.js";
import Footer from "./Footer.js";

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex",
    flexDirection: "column",
    minHeight: "100vh",
  },
  main: {
    marginTop: theme.spacing(0),
    marginBottom: theme.spacing(10),
  },
}));

const theme = createMuiTheme({
  palette: {
    type: "dark",
    background: {
      default: "#212121",
    },
    typography: {
      fontFamily: "Quicksand",
    },
  },
});

function Layout({ children }) {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <ThemeProvider theme={theme}>
        <CssBaseline />

        <Container component="main" className={classes.main} maxWidth="md">
          <NavBar />
          <Divider />
          {children}
          <Divider />
          <Footer />
        </Container>
      </ThemeProvider>
    </div>
  );
}

export default Layout;

But no luck. can anyone please advise?

Thanks In Advance Venk

3 Answers

You can wrap your in _app.js with a ThemeProvider.

Example _app.js:

import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '@material-ui/core'
import theme from '../styles/theme'


export default function App({ Component, pageProps }) {
  const router = useRouter()

  return(
    <AnimatePresence exitBeforeEnter>
      <CartProvider>
        <AuthProvider>
          <ThemeProvider theme={theme}>
            <Header />
            <motion.div key={router.pathname} className="main">
              <Component { ...pageProps } />
            </motion.div>
          </ThemeProvider>
        </AuthProvider>
      </CartProvider>
    </AnimatePresence>
  )
}

Example theme file:

import { createTheme, responsiveFontSizes } from "@material-ui/core"

let theme = createTheme({
  palette: {
    primary: {
      main: '#0277bd',
    },
    secondary: {
      main: '#b2ff59',
    },
    darkGray: {
      main: '#333333'
    },
  },
})

theme = responsiveFontSizes(theme)
export default theme

I'm not sure how you understand the Mui theme concept. Let's just talk about NextJs first.

NextJs is no different from React. To set the global font-family you just have to put font-family: your desired font into the html, body {} block inside the global.css which is already imported at the top level of _app.js. Every component will use that global font unless it has custom styles. Example:

html, body {
  padding: 0;
  margin: 0;
  font-family: Quicksand, cursive;
}

Now, with Mui, components use a designed style system which conclude default font option as well. To inject your custom style into every single Mui's component you need to wrap all of them inside a top/root level <ThemeProvider theme={yourCutomTheme}> component to override the defaults, reference: Custom Theming from Mui official doc.

Things are not that complicated tho.

In Nextjs V10+ with material-ui

_document.js

Don't forget to add this link tag before your fonts for improving the performance

<link rel="preconnect" href="https://fonts.gstatic.com" />

_app.js

add the override object to make your font a global font in your app instead of Roboto the default

const theme = responsiveFontSizes(
  createMuiTheme({
    // your custom UI Config ,
    typography: {
      fontFamily: "Quicksand",
    },
    overrides: {
      MuiCssBaseline: {
        "@global": {
          "@font-face": [
            {
              fontFamily: "Quicksand",
              fontStyle: "normal",
              fontDisplay: "swap",
              fontWeight: // your font weight,
            },
          ],
        },
      },
    },
  })
)
Related