How to host React Applications on NetSuite ERP the right way?

Published on

Nov 18, 2024

Mayank

6 min read

React apps are widely used for single-page applications (SPAs), where the application loads a single HTML page and dynamically updates content without requiring a full page reload. This improves performance and provides a more seamless interaction for users. Additionally, React’s component-based architecture promotes scalability, making it suitable for both small projects and large-scale applications.

React is also well-supported by a vast ecosystem, including libraries for state management like Redux and tools for building mobile apps through React Native. This flexibility allows developers to build apps that work across platforms, from web to mobile. Overall, React’s simplicity, speed, and versatility make it a go-to choice for developers looking to create modern, high-performing web applications.

1. The challenge for React Apps on NetSuite

React, often used to create Single Page Applications (SPAs), comes with its own requirements, such as routing and component-based architecture, which does not naturally align with how NetSuite’s backend infrastructure is traditionally designed.

NetSuite's Backend-Centric Environment

NetSuite is largely backend-oriented, with most of the application logic executed server-side through SuiteScript, which is JavaScript-based but used in a more traditional, server-side model. Hosting a React app on NetSuite means bridging the gap between a system built for backend processes and a frontend system that expects a client-side environment.

Challenge: NetSuite’s SuiteScript and server-side rendering can conflict with the client-heavy approach of React, where most of the application logic and UI rendering is done on the client side.

Potential Solution: Developers may need to rely on external services (such as AWS S3 or other static file hosting services) for delivering the React frontend, while the backend is served by NetSuite.

Routing Issues in a Single Page Application (SPA)

One of React’s core features in an SPA is routing, where different "pages" are displayed without refreshing the browser. This is done client-side using libraries like React Router. However, NetSuite’s traditional page load model doesn't natively support this type of routing.

Challenge: NetSuite expects full page refreshes when navigating between pages, which is contrary to how SPAs typically function. This can cause issues with deep linking and bookmarking, as NetSuite routes requests through its own URL structures.

Potential Solution: To overcome this, you can configure NetSuite to serve a single HTML file for all routes (via SuiteCommerce or custom SuiteScript). React Router will then handle routing client-side. However, this may require some URL rewriting or custom SuiteScript logic to intercept navigation requests and reroute them to the React app's router.

2. Bundling and deploying React JavaScript and CSS

Vite has become a popular choice for building modern React applications due to its lightning-fast development experience and optimized production builds. Vite uses a new approach to bundling, leveraging native ES modules in the browser and lazy-building only the parts of the app that are actually used. This contrasts with traditional bundlers like Webpack, which pre-bundles everything upfront.

Creating a Vite react application (With TypeScript)

npm create vite@latest my-react-app
cd my-react-app
npm install
The default vite.config.ts file is like this:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

To generate single JS and CSS files when using npm run build, change the config file to:

import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
  },
  preview: {
    port: 3000,
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: () => 'index.js',
        entryFileNames: 'assets/[name].js',
        chunkFileNames: 'assets/[name].js',
        assetFileNames: 'assets/[name].[ext]'
      },
    },
    cssCodeSplit: false
  }
});

This will generate JS and CSS files without any random names in the dist directory:


Now, create a new gulpfile.js in the project’s directory with the following contents and install the required packages:

const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
const cheerio = require('cheerio');
const through2 = require('through2');

// Task to process HTML and inject JS
gulp.task('html', () => {
	return gulp.src('./dist/index.html')
		.pipe(htmlmin({ collapseWhitespace: true }))
		.on('error', (err) => console.error('Error in html task', err))
		.pipe(gulp.dest('build'))
		.pipe(injectFiles())
		.pipe(gulp.dest('build'))
		.on('end', () => console.log('HTML processing complete.'));
});

function injectFiles() {
	return through2.obj((file, enc, cb) => {
		if (file.isNull()) {
			cb(null, file);
			return;
		}

		const $ = cheerio.load(file.contents.toString(), { decodeEntities: false });
		// Remove existing tags
		$('script').remove();

		// Append the new tags
		const jsContent = `<script type="module">${getFileContents('./dist/assets/index.js')}</script>`;
		$('head').append(jsContent);
		const cssContent = `<style>${getFileContents('./dist/assets/style.css')}</style>`;
		$('head').append(cssContent);

		file.contents = Buffer.from($.html());
		cb(null, file);
	});
}

function getFileContents(filePath) {
	const fs = require('fs');
	return fs.readFileSync(filePath, 'utf8');
}

// Default task to run all tasks
gulp.task('default', gulp.series('html'));

Add the gulp script npx gulp to build script in package.json:

"scripts": {
    "start": "vite preview",
    "dev": "vite",
    "build": "vite build && npx gulp",
    "test": "jest --bail --testPathPattern=test/"
  }

To create a single HTML file, execute npm run build in the terminal. The build folder looks like this:

And index.html file like this:

This file can be uploaded to NetSuite File Cabinet and its URL can then be referred for React apps.

3. Conclusion

React apps have component based file structure and for deploying them on NetSuite, we can build a single HTML file containing all the JavaScript and CSS contents.
The build can be generated by following these simple steps:

  • Change the default config file to generate JS and CSS files without any random names.
  • Create a gulpfile in the project’s directory to merge the JS and CSS files into the base index.html file.
  • Upload the HTML file into NetSuite’s File Cabinet and the React app is deployed.

React apps are popular for building dynamic, efficient, and responsive user interfaces. Developed by Facebook, React is a JavaScript library that allows developers to create reusable UI components, making code more modular and easier to maintain. One of its key features is the virtual DOM, which optimizes performance by updating only the components that change rather than re-rendering the entire page. This results in faster load times and a smoother user experience.