red house 87
red house 87

Reputation: 2415

react router - 404 in production

I have an app that uses react-router-dom and I have a a basic setup like so:

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { Box, Flex } from '@chakra-ui/react';
import CodeEntry from './pages/CodeEntry';
import Register from './pages/Register';
// import Login from './pages/Login';
import { useUI } from '../state/context/UIContext';
import { GlobalStyle } from '../styles/GlobalStyles';
import Header from './Header';
import Footer from './Footer';
import ErrorMessage from '../components/ErrorMessage';
import routes from '../config/routes';

const App = () => {
  const {
    state: { errorMessage },
  } = useUI();

  return (
    <Router>
      <GlobalStyle />
      <Flex flexDir="column" justifyContent="space-between" minH="100vh">
        <Box>
          <Header />
          {errorMessage.length > 0 && <ErrorMessage message={errorMessage} />}
          <Switch>
            <Route exact path={routes.APP.REGISTER}>
              <Register />
            </Route>
            <Route exact path={routes.APP.DASHBOARD}>
              <>Dashboard</>
            </Route>
            <Route exact path={routes.APP.HOME}>
              <CodeEntry />
            </Route>
            <Route exact path={routes.APP.LOGIN}>
              <>Login</>
            </Route>
          </Switch>
        </Box>
        <Box>
          <Footer />
        </Box>
      </Flex>
    </Router>
  );
};

export default App;

When I run my app locally with npm run dev it uses the following command to spin up a local server:

webpack serve --config webpack.dev.js

the webpack.dev.js file looks like so:

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');

const Dotenv = require('dotenv-webpack');
const plugins = [];
//if (process.env.CI !== 'true') plugins.push(new Dotenv({ systemvars: true, path: path.resolve(__dirname, '.env') }));

module.exports = merge(common, {
  mode: 'development',
  devServer: {
    host: 'localhost',
    port: 3000,
    historyApiFallback: true,
    open: true,
  },
  infrastructureLogging: {
    level: 'error',
  },
  stats: 'none',
  output: {
    publicPath: '/',
  },
  plugins,
});

and the webpack.common.js like so:

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
require('babel-polyfill');

module.exports = {
  entry: {
    main: ['babel-polyfill', './src/index.tsx'],
  },
  devtool: 'source-map',
  output: {
    filename: '[name].[fullhash].js',
    path: path.resolve('./dist'),
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        loader: 'ts-loader',
      },
      {
        test: /\.js$/,
        exclude: [path.resolve(__dirname, 'node_modules')],
        use: [{ loader: 'babel-loader' }],
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              outputPath: 'assets/img/',
            },
          },
        ],
      },
      {
        test: /\.(woff(2)?|ttf|eot|otf)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'assets/fonts/',
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebPackPlugin({
      template: 'index.html',
    }),
    new webpack.ProvidePlugin({
      process: 'process/browser',
    }),
    new CleanWebpackPlugin({
      cleanAfterEveryBuildPatterns: ['dist'],
    }),
    new webpack.DefinePlugin({
      __REACT_DEVTOOLS_GLOBAL_HOOK__: '({ isDisabled: true })',
    }),
  ],
};

Everything in dev works as it should but when I run a build for production non of my routes apart from the root route ('/') work. Here is how I run a production build:

npm run build && webpack --config webpack.prod.js

and here is how I serve that build locally using the npm package serve:

serve dist --no-clipboard --listen ${PORT:-3000}"

My webpack.prod.js file is quite simple and adopts a lot of the webpack config from the webpack.common.js config file. Here is my prod config:

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
//
module.exports = merge(common, {
  mode: 'production',
});

I have tried adding a <Link> component like so:

<Link to='/register'>

The link works fine when I click on it in prod as well as dev, it is just when I navigate directly to the page where I get a 404

Upvotes: 0

Views: 365

Answers (1)

red house 87
red house 87

Reputation: 2415

Solution was simple, in the end it was an issue with the serve package. All I had to do was a a -s flag to the following command:

serve -s dist --no-clipboard --listen ${PORT:-3000}"

Upvotes: 1

Related Questions