Next.js: styled-component overridden on hot reload

Created on 28 Mar 2018  ·  27Comments  ·  Source: vercel/next.js

  • [x ] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior


My page contains this very basic code:

import React from 'react'
import styled from 'styled-components'

const Title = styled.h1`
  color: pink;
  font-size: 50px;
`

export default () => <Title>My page</Title>

When I refresh the page, I expect the Title to be of color pink

Current Behavior


The first time the page is built, the title does appear as pink. When I refresh the page, the pink color is no longer respected.
If I change the color in the code to anything else, and save the file, then the page will update. But again, upon refreshing, the changes are lost again. I have no clue what might be causing this. Any help is appreciated.

Your Environment


| Tech | Version |
|---------|---------|
| next | latest |
| node | |
| OS | arch |
| browser | vivaldi |

good first issue

Most helpful comment

Also having this issue

Edit (solution):

This issue is related to not having the babel config setup correctly. If you look at the official example of NextJS with styled-components you'll notice they have a .babelrc file with:

{
    "presets": ["next/babel"],
    "plugins": [["styled-components", { "ssr": true }]]
}

Then you must add this plugin to the devDependencies in your package.json file

"devDependencies": {
    "babel-plugin-styled-components": "^1.8.0"
},

Then run your install command, yarn install or npm install and then you'll be good to go!

P.S: Be sure to only have one tab open for your localhost dev work!

All 27 comments

I have the same bug, with a different setup. I have a monorepo with two folders :

  • app, with my next.js app
  • ui , with my 'ui library' using styled-components

I have it in an example repo : https://github.com/lucleray/ssr-ui-library

When I update the background color for example, in the ui folder, and save the file, the page updates.
But then, if I refresh the page, the updates are lost.

Is this anything to do with the injection order of the style sheets in <head>?

@ianregister No, the order of the injection in <head> is not related

The problem is that the server and the client don't render the same code for stylesheets

I have the same bug and it is not only on styles. Hot reload will pick up the changes, but refreshing browser does not. I have to run server again (node server.js) in order to pick up the latest update. I am using [email protected], react 16.3 and express for the server.

@lucleray, try this:

Make sure you have babel-plugin-styled-components as a dependency and in your package.json tell babel that you are doing server-side rendering.

Think that this is just a configuration issue you are facing.

package.json
```json
"babel": {
"env": {
"development": {
"presets": ["next/babel"],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true
}
]
]
},
"production": {
"presets": ["next/babel"],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": false
}
]
]
}
}
}

@tvthatsme I added babel-plugin-styled-components in both the ui library and the app itself, but I still have the same issue.

You can check here :
https://github.com/lucleray/ssr-ui-library/blob/master/app/.babelrc
https://github.com/lucleray/ssr-ui-library/blob/master/ui/.babelrc

I'm not sure it is configuration (webpack configuration), or a bug somewhere.

having similar issue where any edits to a component (non styles related) will cause an error "Warning: Prop className did not match. Server:" on subsequent hard page fresh. Need to restart server for it to go away.

@sea129 were you able to fix?

Lol, having the same issue here. @valeeum were you able to solve it?

I did a bit of research on this issue, and I think it might be link some sort of "webpack caching".

I use the code here as an example of the issue : https://github.com/lucleray/ssr-ui-library

The app is using Next.js and has a dependency on the ui. I'm using yarn's workspace to link them but it could be a more simple yarn link.

On the server-side, the bundle does not include the ui module and treats it as external (because it's in node_modules).
On the client-side, the bundle includes the ui module.

It appears like this in the .next/static (client-side) bundles :

/***/ "../ui/bundle.js":
/*!***********************!*\
  !*** ../ui/bundle.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var styled = _interopDefault(__webpack_require__(/*! styled-components */ "../node_modules/styled-components/dist/styled-components.browser.esm.js"));

const UiButton = styled.button`
  background: black;
`;

exports.UiButton = UiButton;


/***/ }),

On the other hand, in the .next/server (server-side) bundles, it appears like this :

/***/ "@monorepo/ui":
/*!*******************************!*\
  !*** external "@monorepo/ui" ***!
  \*******************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = require("@monorepo/ui");

/***/ }),

What is weird is that module.exports = require("@monorepo/ui") should resolve to the ui package and just work fine. But it seems like it's cached and does not resolve to the actual ui package.

If I change the background color of the button in the ui to blue for example. The hot reloader updates the page correctly. But if I refresh, then the page is rendered server-side with the old color (green in my example). It's like the server-side bundle is not resolving the @monorepo/ui dependency to go the actual package, but is using a cached version of it.


It solved it by configuring webpack to treat @monorepo/ui not as an external dependency and bundle it in the server-side bundles, like this : https://github.com/lucleray/ssr-ui-library/blob/webpack-config/app/next.config.js

I would still like to understand what's happening 🤔

require has a built-in cache (require.cache) this is expected behavior for dependencies in node_modules, as we won't bundle anything inside node_modules on the server (for compilation speed).

Ok, that makes sense.

So the issue is that the hot-reloader doesn't reload the code changes in external dependencies, on the server-side, right ?

In the example I shared, I expect my code changes to be loaded both on client (which is the case) and server (which is not the case) side in development mode.

So the issue is that the hot-reloader doesn't reload the code changes in external dependencies, on the server-side, right?

Exactly, we only delete full paths from the require.cache.

How do you think I could solve this @timneutkens ?

Should I remove @monorepo/ui from the external dependencies so it's bundled in the server-side bundles ? That works but since I'm already transpiling it, wouldn't it better to just require it ?

It might be as easy as using https://github.com/martpie/next-plugin-transpile-modules to enable module compilation.

@timneutkens Why was this closed? this issue still exists on the latest version

Still having this issue as well

Im still having this issue too

Ditto too here

Also having this issue

Edit (solution):

This issue is related to not having the babel config setup correctly. If you look at the official example of NextJS with styled-components you'll notice they have a .babelrc file with:

{
    "presets": ["next/babel"],
    "plugins": [["styled-components", { "ssr": true }]]
}

Then you must add this plugin to the devDependencies in your package.json file

"devDependencies": {
    "babel-plugin-styled-components": "^1.8.0"
},

Then run your install command, yarn install or npm install and then you'll be good to go!

P.S: Be sure to only have one tab open for your localhost dev work!

ok people. here is the solution. create an _document.js in root of directory and add this.

import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet } from 'styled-components'

class MyDocument extends Document {
  static getInitialProps ({ renderPage }) {
    const sheet = new ServerStyleSheet()
    const page = renderPage(App => props => sheet.collectStyles(<App {...props} />))
    const styleTags = sheet.getStyleElement()
    return { ...page, styleTags }
  }

  render () {
    return (
      <html>
        <Head>
          <title>Your site title</title>
          {this.props.styleTags}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}

export default MyDocument

then npm install --save -D babel-plugin-styled-components

also create a custom app in pages directory, _App.js and add:

import React from "react";
import App from "next/app";

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return <Component {...pageProps} />;
  }
}

export default MyApp;

you're all set

Also having this issue

Edit (solution):

This issue is related to not having the babel config setup correctly. If you look at the official example of NextJS with styled-components you'll notice they have a .babelrc file with:

{
    "presets": ["next/babel"],
    "plugins": [["styled-components", { "ssr": true }]]
}

Then you must add this plugin to the devDependencies in your package.json file

"devDependencies": {
    "babel-plugin-styled-components": "^1.8.0"
},

Then run your install command, yarn install or npm install and then you'll be good to go!

P.S: Be sure to only have one tab open for your localhost dev work!

This works, Thanks

Unfortunately, it's still an issue.

I am witnessing something weird though:

If you have

styled.h1`
color: red;
`

on the initial load it's red, when you change it to

styled.h1`
color: purple;
`

it gets ignored, text becomes black, no styles.
When you reload the page it gets purple as expected.
But when you then change color to red, HMR works properly and text becomes red.
You can do it with as many colors if you want, like if some kind of caching going on.

So when you add a new style via HMR it gets ignored, but on initial render it's placed in cache, and if you use this style later via HMR it's gonna work. So it's not delivered to browser properly on HMR?

I don't have knowledge on SC and Next.js internals, so those are just my guesses.

EDIT:
it worked ok on styled-components 5.0.0-rc.2

not sure how relevant this info will be, but how i fixed it for myself is i followed the example from next repo and integrated the missing bits a pieces
https://github.com/zeit/next.js/tree/canary/examples/with-typescript-styled-components

I was using next.js and @MaxMcKinney solution worked for me. Just make sure you have configured the .babelrc preferences correctly as it's quite a confusing format or arrays and objects to follow.

This was my .babelrc file for example:

{
    "presets": [
        [
            "next/babel",
            {
                "styled-jsx": {
                    "plugins": [
                        "styled-jsx-plugin-postcss"
                    ]
                }
            }
        ]
    ],
    "plugins": [
        [
            "styled-components",
            {
                "ssr": true
            }
        ]
    ]
}

This issue will occur if you're cloning and building on one of the Next.js examples that don't have the same package.json dependencies and .babelrc settings. In addition to MaxMckinney solution, ensure that you have the correct dependency in package.json. I was outright missing "styled-components": "^5.0.0"

In my case ,just add _app.js and _document.js file under pages floder,it work fine.

// _app.js
import App from 'next/app'
import { ThemeProvider } from 'styled-components'

const theme = {}

export default class MyApp extends App {
    render() {
        const { Component, pageProps } = this.props
        return (
            <ThemeProvider theme={theme}>
                <Component {...pageProps} />
            </ThemeProvider>
        )
    }
}
// _document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
}

In my case ,just add _app.js and _document.js file under pages floder,it work fine.

// _app.js
import App from 'next/app'
import { ThemeProvider } from 'styled-components'

const theme = {}

export default class MyApp extends App {
    render() {
        const { Component, pageProps } = this.props
        return (
            <ThemeProvider theme={theme}>
                <Component {...pageProps} />
            </ThemeProvider>
        )
    }
}
// _document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
}

How should I use it in the Material-UI?

Was this page helpful?
0 / 5 - 0 ratings