Run React Frontend and SpringBoot Backend on the same port and Package them as a single artifact !!
Tags: react, springboot, java, maven
Published: 2021-04-10 19:02:01

### Find out how to combine [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) with Spring Boot to give you a single artifact.

Hey there! I am a full stack developer with 3+ years of experience based out of West Bengal, India. *Today I will walk you through a setup trick to prepare your project structure for running react and spring boot app on the same port, and package them as a single artifact.* You will see how easy this can make development when dealing with both of these game-changing technologies. 

#### Here is the [GitHub](https://github.com/arpan-banerjee7/demo-spring-react-maven-plugin) link of the demo that I am going to explain here.

#Advantages of this setup
1. Combines the ease of development of both [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) and Spring Boot. 

2. You can use the hot reload feature of [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) while developing the frontend, save the page after making changes and it will reflect immediately on the browser.

3. Using Spring Boot you can easily manage your development versions (using SNAPSHOTs) and release versions in production. You can detect build version and time at runtime in Spring Boot using  [Spring Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html) or Spring Admin, which can provide the build metadata functionality plus a lot more.

4. Package Frontend and Backend in a single war/jar file with optimized production build.

I learned this in one of my recent projects. It has several spring boot microservices with react frontend packaged and deployed as single artifacts of their own.

So, here is a quick guide how to run a react frontend and a spring boot backend on the same port and how to package them as a single jar file.

First, **create a spring boot project** with https://start.spring.io. Add the Web dependency. Set the groupId and artifactId to whatever you want. Generate the project and unzip it into your project directory.

Or, if you are using Spring Tools Suite you can simply click 
`File->New->Spring Starter Project` and mention the required details to create a spring boot project.

Your initial pom.xml should look like this.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hopao37ru0jqi3ked9l5.png)
  
The project structure should look like this.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/32we80nbgr4bhp20y62t.png)

You can run the app now and see if it runs without any errors.
Though you won't see anything if you hit http://localhost:8080/.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwacbl7mgf9rcdmf5e8t.png)

Now, go to `src/main/resources`, create a `static` folder. Inside it create a `test.html` file with some text in it.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ys0i7ck74hc89a72yck3.png)

Now, if you restart the application and hit http://localhost:8080/test.html you will see it spins up your html page in *port 8080*. You can see the contents of your file in the browser.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2oioz774lu3u1a7ch1n.png)
 
### Serving the React App from Spring Boot on port 8080 and Packaging them together


*We will exploit the above mentioned feature of Spring Boot to serve the single page of the react project.* We will serve a html page from the `static` folder in the `target` directory, not in the source directory.

Now let's create a react app, using [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html). Navigate to your folder till `src/main` in a terminal
`D:\Codes_projects\springboot_react_maven_plugin\Spring_React_maven-plugin\src\main>` and run `npx create-react-app frontend`.

This should create a react app inside `src/main`. Now if you look at your folder structure it should look like this.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhix2exyhqbqp5r5tyf0.png)

You can run the react app, by doing `cd frontend` and then `yarn start` or `npm start`. It should spin up the react app on http://localhost:3000.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/savbcfnjqro9ndhtsfwu.png)
 
You can create a production build of the frontend by running `yarn build`. Your folder structure after running the build command will look like this.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ce90pom02jdx1u7865nj.png)

You need to copy this production build place it inside the `target/classes` directory of your project to enable Spring Boot to serve this `index.html` when you start your project

So, there are two steps-

1. create a production build of the frontend
2. copy the production build into ${target/classes/}

Of course you are not going to do that manually.
We we will use two `maven plugins` for that.

1. [frontend-maven-plugin](https://github.com/eirslett/frontend-maven-plugin) for step 1.
2. [maven-resources-plugin](https://maven.apache.org/plugins/maven-resources-plugin/) for step 2.

Add the following to your `pom.xml` under the `plugins` section and update the `properties section` as shown.

```
   <properties>
		<java.version>1.8</java.version>
		<frontend-src-dir>${project.basedir}/src/main/frontend</frontend-src-dir>
		<node.version>v14.15.4</node.version>
		<yarn.version>v1.16.0</yarn.version>
		<frontend-maven-plugin.version>1.7.6</frontend-maven-plugin.version>
	</properties>
```

```
           <plugin>
				<groupId>com.github.eirslett</groupId>
				<artifactId>frontend-maven-plugin</artifactId>
				<version>${frontend-maven-plugin.version}</version>

				<configuration>
					<nodeVersion>${node.version}</nodeVersion>
					<yarnVersion>${yarn.version}</yarnVersion>
					<workingDirectory>${frontend-src-dir}</workingDirectory>
					<installDirectory>${project.build.directory}</installDirectory>
				</configuration>

				<executions>
					<execution>
						<id>install-frontend-tools</id>
						<goals>
							<goal>install-node-and-yarn</goal>
						</goals>
					</execution>

					<execution>
						<id>yarn-install</id>
						<goals>
							<goal>yarn</goal>
						</goals>
						<configuration>
							<arguments>install</arguments>
						</configuration>
					</execution>

					<execution>
						<id>build-frontend</id>
						<goals>
							<goal>yarn</goal>
						</goals>
						<phase>prepare-package</phase>
						<configuration>
							<arguments>build</arguments>
						</configuration>
					</execution>
				</executions>
			</plugin>	
```

Now if you run `mvn clean install` from `
D:\Codes_projects\springboot_react_maven_plugin\Spring_React_maven-plugin>`  maven will install npm, yarn and node locally and run npm build in the frontend directory.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g74wfxixpajctpriqgib.png)
 
The folder structure would look like this-

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45meqmlcbpphpxvmdtfb.png)

Now, add the second plugin to your `pom.xml`.


```
          <plugin>
				<artifactId>maven-resources-plugin</artifactId>
				<executions>
					<execution>
						<id>position-react-build</id>
						<goals>
							<goal>copy-resources</goal>
						</goals>
						<phase>prepare-package</phase>
						<configuration>
							<outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
							<resources>
								<resource>
									<directory>${frontend-src-dir}/build</directory>
									<filtering>false</filtering>
								</resource>
							</resources>
						</configuration>
					</execution>
				</executions>
			</plugin>
```

Add the following dependency in the dependencies section.

```
       <dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
```
Modify the `spring-boot-maven-plugin` to include a configuration section.

```
          <plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
```
This will ensure that the frontend build files are copied after they have been generated by `yarn build`.
Now, run `mvn clean install` again and inspect the `target/classes/static` directory from your file manager.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpx5ob6z5ofg1798bm7b.png)
 

It will have the frontend production build files in there.

Your final `pom.xml` will look like this.

```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.springreact</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Run React Frontend and SpringBoot Backend on the same port.</description>
	<properties>
		<java.version>1.8</java.version>
		<frontend-src-dir>${project.basedir}/src/main/frontend</frontend-src-dir>
		<node.version>v14.15.4</node.version>
		<yarn.version>v1.16.0</yarn.version>
		<frontend-maven-plugin.version>1.7.6</frontend-maven-plugin.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId> 
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>com.github.eirslett</groupId>
				<artifactId>frontend-maven-plugin</artifactId>
				<version>${frontend-maven-plugin.version}</version>

				<configuration>
					<nodeVersion>${node.version}</nodeVersion>
					<yarnVersion>${yarn.version}</yarnVersion>
					<workingDirectory>${frontend-src-dir}</workingDirectory>
					<installDirectory>${project.build.directory}</installDirectory>
				</configuration>

				<executions>
					<execution>
						<id>install-frontend-tools</id>
						<goals>
							<goal>install-node-and-yarn</goal>
						</goals>
					</execution>

					<execution>
						<id>yarn-install</id>
						<goals>
							<goal>yarn</goal>
						</goals>
						<configuration>
							<arguments>install</arguments>
						</configuration>
					</execution>

					<execution>
						<id>build-frontend</id>
						<goals>
							<goal>yarn</goal>
						</goals>
						<phase>prepare-package</phase>
						<configuration>
							<arguments>build</arguments>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<artifactId>maven-resources-plugin</artifactId>
				<executions>
					<execution>
						<id>position-react-build</id>
						<goals>
							<goal>copy-resources</goal>
						</goals>
						<phase>prepare-package</phase>
						<configuration>
							<outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
							<resources>
								<resource>
									<directory>${frontend-src-dir}/build</directory>
									<filtering>false</filtering>
								</resource>
							</resources>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>
```

Now you are ready to go! Run the project and go to http://localhost:8080/index.html VOILA!! You have run your React app via Spring Boot. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijr7cxm5255xg1any814.png)
 
### Taking a step further

You can add `rest controllers` and expose an endpoint to be the starting point of the application. Like an `Index Controller` , whenever the application loads, spring boot will spin up the `index.html` file  having the static contents from the optimized build of the frontend project from the `target directory`. So that you can simply visit http://localhost:8080 and not hardcode the `index.html` file name.

Let's do that.
Add a controller, in the following folder structure.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ol3rop2t3ftu4444dx8.png)

```
package com.springreact.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

	@GetMapping("")
	public ModelAndView home() {
		ModelAndView mav=new ModelAndView("index");
		return mav;
	}
	
}
``` 
Run your application and hit http://localhost:8080/.
Did you see anything? No, right!

You have to add another dependency for this to work.

```
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
```

Now, by default `thymleaf` looks for our templates in `src/main/resources/templates`. We can put our templates there and organize them in sub-directories and have no issues.

So, let's try that. Add a html file inside `src/main/resources/templates` named `index.html` and put some content in it.

Run the application and visit http://localhost:8080. You will see the `index.html` from `src/main/resources/templates` being served in *port 8080*.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fvoflqlxj97fea60qvae.png)
 
Now, to change this default behaviour and make `thymleaf` serve static content from another location, you need to add an entry to the `application.properties` file.

```
spring.thymeleaf.prefix=file:///D:///Codes_projects//springboot_react_maven_plugin//Spring_React_maven-plugin//target//classes//static/
```

Here, I have hardcoded the path.

Finally, now if you `update` your project and start it. You can see the react app being served directly on *port 8080*.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pw9ncenszv8fkzhtd6pw.png)

## Conclusion

You have learned how to use spring boot to run a react app. You can run both your frontend and backend on the same port. I have explained what you need to do to get the setup done. You can now create a meaningful application, and see how easy and quick it is to develop with this setup. You can now run the frontend application using React-scripts by running `cd frontend`; `yarn start`, and develop using the hot reload feature of  [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) and have meaningful error messages, while being able to deploy the application to test and production environments as a single artifact. 


Thanks for reading! 

Please leave a comment if you find this helpful. Any feedback or suggestions are welcome.