Resolving the Material-UI NextJS Button Styling Dilemma

I've encountered an issue with the styling of a few buttons in JS. When I apply a styling class using className, the formatting works fine on the initial render but loses its styling on subsequent refreshes. This problem is specific to two individual buttons. After trying various troubleshooting methods, I discovered that switching to SX instead of classNames resolves the issue and the styling persists even after refreshing. In the code below, one button retains its styles while the other does not upon refreshing. I'm puzzled at this point as I've extensively searched through forums and found mentions of NextJs potentially requiring additional configuration in the _document and _app files for it to function properly. However, I used the NextJs Material UI boilerplate from Git, so I doubt that is causing the problem.

Code:

import React from 'react'

import { AppBar, Toolbar, alpha } from "@mui/material";
import Button from '@mui/material/Button'
import ButtonBase from '@mui/material/ButtonBase';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import InputBase from '@mui/material/InputBase';
import SearchIcon from '@mui/icons-material/Search';
import AddIcon from '@mui/icons-material/Add';
import { makeStyles } from '@mui/styles'

const useStyles = makeStyles(theme => ({

    button: {
        ...theme.typography.mainmenu,
        borderRadius: "40px",
        width: "230px",
        height: "130px",
        marginLeft: "30px",
        alignItem: "center",
        "&:hover": {
            backgroundColor: theme.palette.secondary
        },
        [theme.breakpoints.down("sm")]: {
            width: '100% !important', // Overrides inline-style
            height: 100
        },
    },

}))
/*Image Button Styling Begins*/
const images = [
    {
        url: '/assets/breakfastMenu.jpg',
        title: 'Breakfast',
        width: '20%',
    },
    {
        url: '/assets/steak.jpg',
        title: 'Mains',
        width: '20%',
    },
    {
        url: '/assets/desserts.jpg',
        title: 'Desserts',
        width: '20%',
    },
];
const Image = styled('span')(({ theme }) => ({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
    color: theme.palette.common.primary,
}));

const ImageButton = styled(ButtonBase)(({ theme }) => ({
    position: 'relative',
    height: 150,
    [theme.breakpoints.down('sm')]: {
        width: '100% !important', // Overrides inline-style
        height: 100,
    },
    '&:hover, &.Mui-focusVisible': {
        zIndex: 1,
        '& .MuiImageBackdrop-root': {
            opacity: 0.15,
        },
        '& .MuiImageMarked-root': {
            opacity: 0,
        },
        '& .MuiTypography-root': {
            border: '4px solid currentColor',
        },
    },
}));

const ImageSrc = styled('span')({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    backgroundSize: 'cover',
    backgroundPosition: 'center 40%',
});
const ImageBackdrop = styled('span')(({ theme }) => ({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    backgroundColor: theme.palette.common.black,
    opacity: 0.4,
    transition: theme.transitions.create('opacity'),
}));

const ImageMarked = styled('span')(({ theme }) => ({
    height: 3,
    width: 18,
    backgroundColor: theme.palette.common.white,
    position: 'absolute',
    bottom: -2,
    left: 'calc(50% - 9px)',
    transition: theme.transitions.create('opacity'),
}));
/*Image Button Styling Ends*/

const Search = styled('div')(({ theme }) => ({
    position: 'relative',
    borderRadius: theme.shape.borderRadius,
    backgroundColor: alpha(theme.palette.common.white, 0.15),
    '&:hover': {
        backgroundColor: alpha(theme.palette.common.white, 0.25),
    },
    marginLeft: 0,
    width: '100%',
    [theme.breakpoints.up('sm')]: {
        marginLeft: theme.spacing(1),
        width: 'auto',
    },
}));

const SearchIconWrapper = styled('div')(({ theme }) => ({
    padding: theme.spacing(0, 2),
    height: '100%',
    position: 'absolute',
    pointerEvents: 'none',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
}));

const StyledInputBase = styled(InputBase)(({ theme }) => ({
    color: 'inherit',
    '& .MuiInputBase-input': {
        padding: theme.spacing(1, 1, 1, 0),
        // vertical padding + font size from searchIcon
        paddingLeft: `calc(1em + ${theme.spacing(4)})`,
        transition: theme.transitions.create('width'),
        width: '100%',
        [theme.breakpoints.up('sm')]: {
            width: '12ch',
            '&:focus': {
                width: '20ch',
            },
        },
    },
}));


const Header = () => {
    const classes = useStyles();
    return (<React.Fragment>
        <AppBar position="sticky" className={classes.appBar}>
            <Toolbar disableGutters>

                {images.map((image) => (
                    <ImageButton
                        focusRipple
                        key={image.title}
                        style={{
                            width: image.width,
                        }}
                    >
                        <ImageSrc style={{
                            backgroundImage: `url(${image.url})`
                        }} />
                        <ImageBackdrop className="MuiImageBackdrop-root" />
                        <Image>
                            <Typography
                                component="span"
                                variant="subtitle1"
                                color="white"
                                fontWeight="bold"

                                sx={{
                                    position: 'relative',
                                    p: "7em",
                                    pt: "2em",
                                    pb: (theme) => `calc(${theme.spacing(1)} + 6px)`,
                                }}
                            >
                                {image.title}
                                <ImageMarked className="MuiImageMarked-root" />
                            </Typography>
                        </Image>
                    </ImageButton>
                ))}
                <Button size="large" variant="contained" color="secondary"
                    startIcon={<AddIcon />}
                    sx={{
                        borderRadius: "40px", borderRadius: "40px",
                        width: "230px",
                        height: "130px",
                        marginLeft: "30px",
                        alignItem: "center",
                    }} >Add A recipe</Button>
                <Button size="large" variant="contained" color="secondary" className={classes.button}>Meals for the Week</Button>
                <Search>
                    <SearchIconWrapper>
                        <SearchIcon />
                    </SearchIconWrapper>
                    <StyledInputBase
                        placeholder="Search…"
                        inputProps={{ 'aria-label': 'search' }}
                    />
                </Search>
            </Toolbar>
        </AppBar>

    </React.Fragment >
    )
}

export default Header

https://i.stack.imgur.com/Cckj8.jpg

Answer №1

To ensure proper styling with NextJS server side rendering, it's crucial to add configuration to the _document.tsx file. This is necessary because certain styles need to be injected into the DOM for effective rendering.

The MUI documentation explains how you can utilize the ServerStyleSheets feature to manage server side rendering correctly.

Below is an example of the code found in my _document.tsx:

import React from 'react';
import Document, { Html, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@mui/styles';

export default class MyDocument extends Document {
    render() {
        return (
            <Html>
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

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: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()]
    };
};

For more details on this topic, refer to the Server rendering - MUI docs.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way for me to implement a functionality where clicking on a favorite button will result in the character's name being appended to a dropdown list using React?

I am working on a page that displays cards with information and includes a like button. When the like button is selected, I want to save the card as a favorite and have it displayed in a dropdown list along with all the other favorites. export const CardCh ...

Stranger things happening when incorporating a generator function in React

Here's a simplified version of my component. It includes a generator function that cycles through values. const App = () => { const [state, setState] = useState("1") function* stateSwitch () { while (true){ yield "2" yield "3" ...

CSS3 Marquee Effect - puzzled

After trying numerous solutions for the marquee effect, I find myself at a dead end. Even webkit examples have failed me, as the demos don't seem to work either. My browser of choice is Chrome, but my website needs to function properly in both Chrome ...

Having trouble with reactjs and typescript? Getting the error message that says "Type 'string' is not assignable to type 'never'"?

When trying to setState with componentDidMount after an axios request is completed, you may encounter the error message Type 'string' is not assignable to type 'never'. Below is the code snippet: import * as React from 'react&apos ...

Issue with Attaching Click Event to Dynamic Div Elements

I've implemented divs with a Click Event triggered by a value entered in a text box. See an Example Here Upon opening the page, clicking any rows will trigger an alert. However, if you change the value in the text box (Enter Number) and click load, ...

An issue with Axios request in a cordova app using a signed version

Currently, I am in the process of developing a Cordova application utilizing Axios and React. The interesting part is that everything runs smoothly when I build the app with Cordova and test it on my phone using the APK. However, once I sign, zipalign it, ...

Is there a way to adjust the sizes of images within a flexbox layout?

I am currently working on developing a website for my portfolio and I want to achieve a similar layout to this: https://i.stack.imgur.com/giJNF.png However, I am facing some difficulties with displaying the Twitter/Instagram and Deviantart icons. I am usi ...

Step-by-step guide on eliminating the modal post-validation

Newbie in reactjs looking for help with modal validation issue. Goal: Press submit button inside modal, validate, then close the modal. Want to reuse modal for another row. Problem: I'm having trouble making the function work for a new row after ...

jquery toggle for partial visibility not functioning properly

Can jquery sliding functions be used to partially show and hide content? In this scenario, there is a list with 7 items but only the first two are visible initially, while the rest are hidden. The goal is to have all 7 items show when the user clicks to v ...

Mental stability- Next.js constantly updating information

I am currently developing a social media app using next.js with sanity as the CMS. One of the key features I want to implement is real-time updates, similar to how Twitter works. Essentially, when someone on another device likes a post, it should automatic ...

The utilization of the "notFound" prop within getStaticProps does not influence the HTTP status code returned by the page

I recently set up a fresh Next.js application and created the following page: // /pages/articles/[slug].js import React from 'react' import { useRouter } from 'next/router' import ErrorPage from 'next/error' const Article = ...

The object in three.js disappears from the scene but remains visible

I am attempting to showcase text as a sprite in three.js and aim to move the sprite along with an object. I achieve this by utilizing a canvas to generate a texture, which is then mapped using SpriteMaterial to create a sprite from it. However, when I remo ...

Issue with Flask: Data sent via render_template not being rendered in HTML

I have a query set up to access my database, create an object, and then pass it to my HTML using render_template. The problem I'm facing is that the information is not being displayed on the HTML page. Relevant code: @app.route('/profile', ...

Update the CAPTCHA, while also refreshing the entire registration form

JavaScript Snippet <script language="javascript" type="text/javascript"> function updateCaptcha() { $("#captchaimage").attr('src','new_captcha_image.php'); } </script> New Image Code <img src="new_ ...

Checking for the Existence of a Database Table in HTML5 Local Storage

Upon each visit to my page, I automatically generate a few local database tables if they do not already exist. Subsequently, I retrieve records from the Actual Database and insert them into these newly created local tables. I am wondering if there is a me ...

How can I retrieve the input value on the current page using PHP?

Hey there, so I'm pretty new to PHP and I have a question. Is it possible to retrieve the input value from an existing input field on a page using PHP when the page loads, and then assign that value to a variable? For instance, let's say I have ...

Can anyone explain why the background images aren't appearing correctly on iPhones and iPads?

I've been attempting to resolve the issues on this page for what seems like forever. The site in question is theafropick.co.uk. All devices seem to function properly except for iPads and iPhones, where the section with 6 tiles disappears entirely. ...

What is the method for defining the initial number of rows per page in material-react-table?

I am having trouble adjusting the default number of rows per page in material-react-table. Despite following the instructions in the documentation to customize the rowsPerPage options, the initial rows per page setting remains fixed at 10 even after remov ...

Is there a lack of a feature for automatically shifting to the next input element in dynamically-created HTML?

I have a JavaScript function that is triggered when a user clicks a button: htmlstring = ''+ '<div class="input_holder">'+ '<div class="as_input_holder"><input tabindex="1" class="as_input form-control-sm ...

Unable to retrieve the sum total of all product items and display it in the designated text element

My product items are dynamically generated in a list. I have used the calculateItemTotal() method to determine the total for each item. Now, I need to sum up all these item totals and display the result in the total text field. However, instead of showing ...