In this tutorial, we’ll be creating a React application with routes and then deploy it on an Apache server.
1. Introduction
Starting off, let’s explore the differences between putting together a Single Page Application (SPA) with routes and navigating the realm of Multi-Page Applications (MPA).
Multi-Page Applications (MPA)
Emerging from the realms of traditional web development technologies, such as PHP, developers were accustomed to developing Multi-Page Applications (MPA). In an MPA setup, the website comprised distinct HTML or PHP files neatly organized in folders. Each folder seamlessly corresponded to a specific route in the web browser.
To illustrate, consider the following structure in an MPA:
/index.php
/about/index.php
/contact/index.php
Now, let’s envision our scenario where PHP files are deployed on an Apache server within our local machine, delivering content through localhost on port 80. If we were to open the browser, we’d find ourselves:
- Visiting the home page in http://localhost/
- Visiting the about page in http://localhost/about/
- Visiting the contact page in http://localhost/contact/
Each time we visit one of the designated routes, the browser diligently fetches the corresponding PHP file nestled within the associated folder.
This seamless interaction ensures that actions like refreshing the page or navigating through the page history unfold effortlessly, akin to the intuitive experience of traversing directories in a file explorer.
Single Page Applications (SPA)
The dynamics shift when delving into Single Page Applications (SPAs), such as React apps. In the SPA realm, it’s customary to have the entire application served from a single HTML file. A bundled SPA would take on a form similar to this:
/index.html
/index.js
In this structure, the index.js
stands as the bundled JavaScript file, encompassing the entirety of the application.
This architectural choice proves beneficial for loading times as it ensures that all necessary files are loaded during the initial visit to the web page. This optimization plays a key role when navigating to different routes in the browser.
While this optimization brings notable benefits, it does come with its own set of drawbacks. In SPAs, the responsibility of routing typically falls on a front-end routing library, simulating the traditional routing experience. This arrangement, however, introduces certain challenges when it comes to deploying the application on a server.
2. Implementation
In this section, we’ll embark on the journey of creating a straightforward React app with routing. Brace yourself for the next installment, where we’ll navigate through a common pitfall encountered during the deployment phase.
React App & basic components
If you’re eager to code along, kickstart the process by cloning this repository and checking out the initial commit:
$ git checkout ffb6684c551
Rename the App.tsx
to Home.tsx
:
import React from "react";
export function Home() {
return (
<div className="main">
<h1>Home</h1>
</div>
);
}
This will represent our “home” page.
We can now create two additional components for the “about” and the “contact” pages:
/src/About.tsx
import React from "react";
export function About() {
return (
<div className="main">
<h1>About</h1>
</div>
);
}
/src/Contact.tsx
import React from "react";
export function Contact() {
return (
<div className="main">
<h1>Contact</h1>
</div>
);
}
Install React Router & create routes
For this example, let’s employ React Router version 6 and set up a browser router. The browser router stands as the recommended choice for a majority of web projects, leveraging the DOM History API that seamlessly aligns with most modern browsers.
The History API uses functions like pushState and replaceState to manipulate the browser history. As a quick recall from our previous discussion, incorporating this level of complexity into SPAs serves as the trade-off required to simulate the traditional routing found in MPAs. The upside to this added complexity manifests in accelerated loading times and enhanced performance.
In order to use React Router, we will first need to install it:
$ npm install react-router-dom
Then use the createBrowserRouter function to configure the routes in the index file:
/src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { Home } from "./Home";
import "/public/style.css";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { About } from "./About";
import { Contact } from "./Contact";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "/about",
element: <About />,
},
{
path: "/contact",
element: <Contact />,
},
]);
ReactDOM.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
document.getElementById("app")
);
We can verify that our application works fine if we start the development server:
$ npm start
If we visit http://localhost:8080/ we will see a blue “Home” heading, telling us that we are in the “home” route.
If we visit the http://localhost:8080/about page we expect to see the “About” heading, but something is wrong here:
The server cannot deliver us the GET request to the /about route, but why did this happen?
Deploy the app
The server hiccup occurs because the webpack dev server isn’t aware that we’re aiming for the /about React routing route.
To resolve this, add the following to your webpack.config.js file:
devServer: {
historyApiFallback: true,
}
Now after we rerun the webpack server we can visit:
- http://localhost:8080/about and see the “About” blue heading
- http://localhost:8080/contact and see the “Contact” blue heading
- http://localhost:8080/other and see the default 404 page from react router!
To tackle server deployment nuances, we’ll need a config file directing all routes to the index route. This savvy move ensures React Router can gracefully handle browser routing without missing a beat.
Apache server
Maybe the most popular web server out there is the Apache server.
Customizing our path to /routing/, we must now update the React Router configuration in index.tsx to acknowledge this base path:
/src/index.tsx
const router = createBrowserRouter(
[
{
path: "/",
element: <Home />,
},
{
path: "/about",
element: <About />,
},
{
path: "/contact",
element: <Contact />,
},
],
{ basename: "/routing" }
);
If we build the React App with:
$ npm run build
We will notice some static HTML and JavaScript files inside the dist folder:
These files can be deployed into any web server, sounds simple.
In an Apache server, just create a “routing” folder inside htdocs (the go-to for handling HTTP requests with static files), then, sit back and witness the seamless enchantment unfold.
The http://localhost/routing/ address works:
But the http://localhost/routing/about not:
Unfortunately, the default Apache “not found” page might make an appearance. This time around, it’s because the Apache server, much like its webpack dev server counterpart, struggles to grasp our intention to access the index.html file when venturing into the /about/ route.
To address the issue, place in an .htaccess configuration file right alongside our index.html within the “routing” folder. This config acts as our guidebook for teaching Apache to tap into the index.html file when we’re navigating routes, leaving the routing job to browser routing.
/htdocs/routing/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /routing/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /routing/index.html [L]
</IfModule>
If we now visit the http://localhost/routing/about we will see the correct message:
We just deployed our client routing powered React app into Apache! This is a reason to celebrate!
3. Conclusion
In this tutorial we learned how to handle Routing in a React application and the difference between routing in MPA and SPA. Then, we learned how to configure webpack and the Apache web server to handle incomming requests for routes that are not the base route.
I trust this tutorial served up some valuable insights into deploying React Apps with routes. Keep the learning momentum going by following Codelanders on Twitter—your go-to source to stay in the loop and never miss a beat!