Creating a React App Without Create-React-App

Here's how to create a full-stack React application that runs on a single web server in a few easy steps without using create-react-app.

Do you find yourself having to run two web servers to power your React website - one for the front-end built with create-react-app and one for the back-end powered by Node.js and Express? Do you sometimes experience complications and downtime due to your two-server setup?

In this article, we explain how to:

  • Create a React app from scratch instead of using create-react-app
  • Streamline your setup to run your full-stack React website on a single web server port
  • Use Babel and Webpack to allow the browser to access and read your React code

Create your project directories

To get started, create a project folder and change into the new directory.  In this example, we will call it full-react-site. Use the following shell commands:

mkdir full-react-site
cd full-react-site

Next, create three folders organize your code:

mkdir public
mkdir src
mkdir server

Now initialize the directory with npm:

npm init -y

Install the prerequisite packages

You will also need to install the necessary packages for React, Express, Webpack and Babel:

npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli
npm install express react react-dom

So far we have installed the packages required to load React and JSX, but nothing else.  If you would like to use Sass or CSS in your React app, make sure your React code can load them by installing these packages:

npm install --save-dev css-loader sass-loader node-sass

Set up Babel

Now make a new file in your project folder called .babelrc with the following content:

{
    "presets": ["@babel/preset-env","@babel/preset-react"]
}

Create your React entry point

Now, before setting up Webpack, you will need to create a React entry point and a very basic HTML page to load your compiled React bundle.  Create a file called entry.jsx in your app's src folder and add this content:

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App'

const Entry = () => {
    return (
    	<App/>
    );
};

ReactDOM.render(<Entry/>,document.getElementById("root"));

As you can see, the code is looking for App in the App.js or App.jsx file. Create the file App.jsx in the same directory as your entry.jsx file and add this content:

import React,{ useState } from 'react';
import './style.scss'; // My Sass Stylesheet

const App = () => {
	let [title, setTitle] = useState("Full-Stack React Site")
	
	function changeHandler(event){
		setTitle(event.target.value);
		document.title=title;
	}
	return (
	  <div className="react-app">
	  	<div className="title-app">
	  		<h1>{title}</h1>
	  		<div className="title-app__changer">
	  			<label htmlFor="changer">Change the title!</label>
	  			<input type="text" id="changer" value={title} onChange={changeHandler} />
	  		</div>
            <p>Hope you are having a nice day.</p>
	  	</div>
	  </div>
	)
}

export default App;

The above is just a simple React app that will change the title of the webpage based on the input entered. Because our entry file entry.jsx imports this App.jsx file, the App.jsx file and its dependencies will be loaded into a bundle by Webpack, ready to be loaded when the App component itself is loaded.

Add a stylesheet

As for the stylesheet, style.scss simply needs to look something like this:

.title-app{
	width: 600px;
	height: 400px;
	padding: 30px;
	background: forestgreen;
	
	h1{
		color: gold;
		font-size: 32px;
	}
	
	label, p{
		font-size: 18px;
		color: lightcyan;
	}
	&__changer{
		input{
			font-size: 20px;
			border-radius: 5px;
			border: 3px dashed darkorchid;
		}
	}
}

Load the compiled Webpack bundle

We still need something that will load the compiled Webpack bundle. To do this, create a very simple HTML file in the server directory called base.html to load your bundle file. The content of base.html should be something like this:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Full-Stack React Website</title>
    </head>
    <body>
        <div id="root"></div>
        <script defer src="/bundle.js"></script>
    </body>
</html>

That simple.

Configure Webpack

To get started configuring Webpack, go to the base of your project directory where package.json and .babelrc are located and create a file called webpack.config.js. This file need to have some basic settings to get your project working, but can also be used to fine-tune your project settings.

Your entry file is src/entry.jsx and your compiled output should be placed into the public folder - let's give it a filename of bundle.js. You also need to ensure that Webpack loads .jsx files with babel-loader, Sass files with sass-loader and CSS files with css-loader.

The following webpack.config.js file does all those things:

const path = require('path');

module.exports = {
	mode: 'development',
	entry: './src/entry.jsx',
	output: {
		path: path.resolve(__dirname,'public'),
		filename: 'bundle.js'
	},
	module: {
		rules: [
			{
				test: /\\.(js|jsx)$/,
				loader: 'babel-loader'
			},
			{
				test: /\\.(scss|sass)$/,
				loader: 'sass-loader'
			},
			{
				test: /\\.(css)$/,
				loader: 'css-loader'
			}
		]
	},
    resolve: {
        extensions: ['jsx','scss','css','js']
    }
};

Create a Node-Express server

To create your Node-Express server, create a file called server.js in your server directory. This file will work as a simple Node.js and Express server.

Requests to your API will be answered with a simple JSON message, while all other requests will load base.html. The files in your public directory will be accessible to anyone, so a reference to /bundle.js will load your webpack bundle (public/bundle.js) once it has been compiled.

Here is the server.js code:

const express = require('express');
const path = require('path');
const app = express();

const port = 8000;

app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(express.static(path.resolve(__dirname,'../public')));

// Now for our API route
app.use('/api',(req,res,next)=>{
    console.log("Someone went to the API");
    res.json({message: "This is the API"});
});

// Because they must have missed every other route, we send the bundle loading file.
app.use((req,res,next)=>{
    console.log("Someone used the front-end");
    res.sendFile(path.resolve(__dirname,'/base.html'));
})

app.listen(port,()=>{
    console.log(`App now listening on port ${port}`);
});

Add build and start commands

Now, to finish, add build and start commands to package.json so that you can use "npm run build" and "npm start" to run the app. The following code will accomplish this for you:

...
"scripts": {
	"build": "webpack",
	"start": "node ./server/server.js"
},
...

Start your app with API

To build and run your website app with separate API, type the following shell comments from within the root folder of your project:

npm run build
npm start

You should then be able to load your React-driven website and access your API through the following URLs:

React website:
http://localhost:8000

JSON API:
http://localhost:8000/api

Final comments

If you have followed the above instructions, then hopefully you have built a full-stack MERN-ready project from scratch.

During the above process, Webpack combined and compressed React, React DOM and our React code into public/bundle.js. The React application can then be loaded by any HTML page containing a div with an id set to load the file.

While it's understandable that developers use simple tools like create-react-app, knowing how to build apps from scratch gives you the knowledge you need to create full stack applications.

Learning how these small parts of the system work will make you a better developer because you will be in a stronger position to solve problems reliably. Understanding how the app construction process works also empowers you with the choice of using pre-built solutions versus creating your own solution.

You've successfully subscribed to The VN Vine
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.