Taylor King
Taylor King

Reputation: 811

CSS styles disappear after refresh in React app

I'm at a loss when it comes to figuring out why this is happening. Right now my hunch is that this is a Webpack issue. I don't have this refresh problem on the dev environment, but only when I build and run that code.

Basically, when I'm on certain pages, and I refresh the app ... all the styles I have in a stylesheet are being ignored, and when I check the response it is actually just showing my index.html page.

Network Response

I am importing the styles into the root of my app, so in the index.js. I know it is still going through that on refresh cause that's where my persistedState logic is happening, and what allows the refresh to work.

Here is my webpack config for production environment. I feel like that has to be the problem somewhat, since the dev environment is just fine. Unless I'm not thinking of other potential problems?

import webpack from 'webpack';
import path from 'path';
import ExtractTextPlugin from 'extract-text-webpack-plugin';

const GLOBALS = {
  'process.env.NODE_ENV': JSON.stringify('production')
};

export default {
  debug: true,
  devtool: 'source-map',
  noInfo: false,
  entry: [
    './src/index'
  ],
  target: 'web',
  output: {
    path: __dirname + '/dist', // Note: Physical files are only output by the production build task `npm run build`.
    publicPath: '/',
    filename: 'bundle.js'
  },
  devServer: {
    contentBase: './dist'
  },
  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin(GLOBALS),
    new ExtractTextPlugin('styles.css'),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin()
  ],
  module: {
    loaders: [
      {test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel']},
      {test: /(\.css)$/, loader: ExtractTextPlugin.extract("css?sourceMap")},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'},
      {test: /\.(woff|woff2)$/, loader: 'url?prefix=font/&limit=5000'},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
      {test: /\.(jpe?g|png|gif|svg)$/i, loader: "file"},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'}
    ]
  }
};

Would love anyones thoughts or solutions to this problem!

EDIT:

Here is the webpack dev config:

import webpack from 'webpack';
import path from 'path';

export default {
  debug: true,
  devtool: 'cheap-module-eval-source-map',
  noInfo: false,
  entry: [
    //'eventsource-polyfill', // necessary for hot reloading with IE
    'babel-polyfill',
    'webpack-hot-middleware/client?reload=true', //note that it reloads the page if hot module reloading fails.
    './src/index'
  ],
  target: 'web',
  output: {
    path: __dirname + '/dist', // Note: Physical files are only output by the production build task `npm run build`.
    publicPath: '/',
    filename: 'bundle.js'
  },
  devServer: {
    contentBase: './src'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [
      {test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel']},
      {test: /(\.css)$/, loaders: ['style', 'css']},
      {test: /\.(jpe?g|png|gif|svg)$/i, loader: "file"},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'},
      {test: /\.(woff|woff2)$/, loader: 'url?prefix=font/&limit=5000'},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'}
    ]
  }

};

Head tag of HTML:

<head>
    <meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
    <link rel="shortcut icon" href="favicon.ico">
    <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700|Roboto:300,400,900" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Work+Sans:300,400,500|Work+Sans:300,400,500" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/icon?family=Material+Icons">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css">
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
  </head>

Upvotes: 12

Views: 18617

Answers (2)

Chirag Joshi
Chirag Joshi

Reputation: 613

Use this - in case of CSS not working on refresh or hard reload

<link rel="stylesheet" href="%PUBLIC_URL%/assets/css/style.css">

instead of

<link rel="stylesheet" href="assets/css/style.css">

Upvotes: 1

Michael Jungo
Michael Jungo

Reputation: 33010

You are using a relative path to the CSS file in your HTML:

<link rel="stylesheet" href="styles.css">

When you visit /example/page/ you're trying to request /example/page/styles.css, but as that doesn't exist, your server falls back to index.html. You're using that behaviour because you always want to serve the same page and your JavaScript will decide what it should render.

Similarly to how you always request /bundle.js, you also want to request /styles.css regardless of the actual URL.

<link rel="stylesheet" href="/styles.css">

This issue only appears in your production build, because you are using style-loader in development, which injects the CSS into a <style> tag from your JavaScript.

Upvotes: 29

Related Questions