6 Ways To Optimize React App Performance Served by a Go Backend
Published: 2024-01-10 05:38:43

Check out more articles:

1. [Building a Scalable Notification System with gRPC and Microservices](https://dev.to/suprsend/building-a-scalable-notification-service-with-grpc-and-microservices-l6d)
2. [React App is Slow? Here's What You Can Do.](https://dev.to/nikl/react-is-slow-what-to-do-now-369g)
4. [A Complete Guide on Notification Infrastructure for Modern Applications in 2023](https://dev.to/suprsend/a-complete-guide-on-notification-infrastructure-for-modern-applications-in-2023-13b9)

---

## Identifying Bottlenecks:
Before diving into optimizations, it's essential to identify the bottlenecks affecting the app's initial load time. Consider metrics like Time to First Byte (TTFB), Largest Contentful Paint (LCP), and server-side response times. With those data in hand, let's test out some strategies.

## Caching Strategy:

### Cache Control in Go:

To optimize the caching of React and third-party library bundles, implement cache control in the Go HTTP headers. The goal here is to cache React libraries and third-party libraries separately, if they form the majority of the frontend code. Conditionally set cache control based on the type of bundle requested.

```go
package main

import (
	"net/http"
	"strings"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// Handle other routes...

		if strings.Contains(r.URL.Path, "/react-bundle/") {
			// Set cache control for React bundles
			w.Header().Set("Cache-Control", "public, max-age=604800") // 1 week
		} else if strings.Contains(r.URL.Path, "/third-party-bundle/") {
			// Set cache control for third-party bundles
			w.Header().Set("Cache-Control", "public, max-age=2592000") // 1 month
		}

		// Serve the requested file...
	})

	http.ListenAndServe(":8080", nil)
}
```

This code snippet demonstrates how to conditionally set cache control based on the requested bundle type.

## Gzip Compression:

### Pre-compression with Webpack:

Explore pre-compressing JS files with a Webpack plugin to enhance gzip compression. This example integrates the CompressionPlugin to pre-compress JS and CSS files. Though it's impact may be marginal.

```javascript
// webpack.config.js
const CompressionPlugin = require("compression-webpack-plugin");

module.exports = {
  // Other configurations...
  plugins: [
    new CompressionPlugin({
      filename: "[path][base].gz",
      algorithm: "gzip",
      test: /\.js$|\.css$/,
      threshold: 10240,
      minRatio: 0.8,
    }),
  ],
};
```

This webpack.config.js snippet shows how to integrate the CompressionPlugin to pre-compress JS and CSS files.


## Image Optimization:

Optimizing image sizes is crucial for improving the Largest Contentful Paint (LCP) metric. Large images contribute significantly to slow load times. Tools like Squoosh provide a user-friendly interface for compressing and resizing images effectively.

### Implementation:
1. **Squoosh Integration:**
   - Visit [Squoosh](https://squoosh.app/) and upload images.
   - Experiment with compression settings to find the right balance between image quality and file size.
   - Download the optimized images and replace the existing ones in your React app.

2. **Webpack Image Compression:**
   - Integrate image compression directly into your Webpack configuration.
   ```javascript
   // webpack.config.js
   module.exports = {
     // Other configurations...
     module: {
       rules: [
         {
           test: /\.(png|jpe?g|gif)$/i,
           use: [
             {
               loader: 'image-webpack-loader',
               options: {
                 mozjpeg: {
                   progressive: true,
                   quality: 65,
                 },
                 optipng: {
                   enabled: false,
                 },
                 pngquant: {
                   quality: [0.65, 0.90],
                   speed: 4,
                 },
                 gifsicle: {
                   interlaced: false,
                 },
               },
             },
           ],
         },
       ],
     },
   };
   ```
   This example demonstrates how to integrate image compression into your Webpack build process.

## Profiling and Benchmarking:

Profiling and benchmarking are essential for identifying performance bottlenecks and ensuring ongoing improvements. Continuous integration (CI) integration helps monitor performance over time.

### Implementation:
1. **Go Benchmarking:**
   - Create benchmark tests for critical functions in your Go backend.
   - Integrate benchmarks into your CI/CD pipeline.
   - Example benchmark:
   ```go
   package main

   import (
     "testing"
   )

   func BenchmarkExampleFunction(b *testing.B) {
     for i := 0; i < b.N; i++ {
       // Call the function to be benchmarked
       ExampleFunction()
     }
   }
   ```
   Run benchmarks using `go test -bench=.`.

2. **Frontend Benchmarking:**
   - Utilize tools like [Lighthouse CI](https://github.com/GoogleChrome/lighthouse-ci) for benchmarking frontend performance.
   - Integrate Lighthouse CI scripts into your CI/CD pipeline.
   - Example Lighthouse CI script:
   ```yaml
   # .lighthouseci.yml
   ci:
     collect:
       url: "http://your-app-url"
       settings:
         emulatedFormFactor: "desktop"
     assert:
       assertions:
         - ["performance-budget", "speed-index", ">= 0"]
   ```
   Customize the script based on your performance goals.

## NGINX Frontend:


NGINX can serve as a frontend proxy for the Go backend, handling static content, cache control, and pre-compressed files.

### Implementation:
1. **NGINX Configuration:**
   - Install NGINX and configure it to proxy requests to your Go backend.
   - Set up static file serving and implement cache control.
   - Example NGINX configuration:
   ```nginx
   server {
     listen 80;
     server_name your-domain.com;

     location / {
       proxy_pass http://localhost:8080; # Your Go backend address
       # Additional proxy settings...
     }

     location /static/ {
       alias /path/to/your/static/files;
       # Cache control settings...
     }
   }
   ```
   Customize the configuration based on your specific setup.

## Client-Side A/B Testing:

Conducting A/B testing on the client side involves temporarily disabling scripts or third-party integrations to identify their impact on performance.

### Implementation:
1. **A/B Testing Framework:**
   - Use an A/B testing framework like [Optimizely](https://www.optimizely.com/) or [Google Optimize](https://optimize.google.com/).
   - Create experiments that involve temporarily disabling specific scripts or integrations.
   - Analyze performance metrics for each variant to identify bottlenecks.

## Monitor Performance:

Monitoring performance using tools like Chrome's Lighthouse and Google PageSpeed Insights is crucial. Focusing on the p99 (99th percentile) helps identify the slowest requests in the system.

### Implementation:
1. **Lighthouse and PageSpeed Insights:**
   - Regularly run Lighthouse and PageSpeed Insights tests on your React app.
   - Analyze the generated reports for suggestions and areas of improvement.
   - Focus on the p99 metric to address the slowest requests and optimize accordingly.

Regularly revisit and adapt these optimizations as your application evolves.

---

> Similar to this, I personally run a developer-led community on Slack. Where we discuss these kinds of implementations, integrations, some truth bombs, weird chats, virtual meets, and everything that will help a developer remain sane ;) Afterall, too much knowledge can be dangerous too.

> I'm inviting you to join our free community, take part in discussions, and share your freaking experience & expertise. You can fill out this form, and a Slack invite will ring your email in a few days. We have amazing folks from some of the great companies (Atlassian, Scaler, Cisco, IBM and more), and you won't wanna miss interacting with them. [Invite Form](https://forms.gle/VzA3ST8tCFrxt39U9)


---
You may want to check out a seamless way of integrating your [notification infrastructure](https://docs.suprsend.com/docs/getting-started) into your applications without needing code & maintenance. Available in all popular languages (Go, Node, Java, Python) with frontend support (React, Angular, Flutter).

{% github suprsend/suprsend-go %}