Two React apps in the same S3 Bucket.
Tags: react, aws, s3, javascript
Published: 2023-03-10 11:29:43

These days ago, I had to search for information and investigate whether two React applications could coexist in the same S3 bucket, without using any other AWS service except the S3 bucket.

I will say that this is not a good practice and only in certain cases like the one that has been presented to me would it be a solution.

The idea is to be able to jump between the two applications once both have been deployed to the bucket. To do this, the first step would be to create a folder structure that facilitates understanding as much as possible.

```
./react-apps
	/original-app #### Original react app
	/nested-app #### Nested react app
```

For both projects ( _**original-app**_ and _**nested-app**_) we will use [Vite + React](https://vitejs.dev/guide/). So inside the **_"/react-apps"_** folder :

`npm create vite@latest original-app -- --template react`
`npm create vite@latest nested-app -- --template react`

Within each project we will find the file _**"vite.config.js"**_ which we have to adjust in both cases. First we'll go with the configuration file of the _**"original-app"**_ project:

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  base: '/',
  build: {
    outDir: '../dist',
    emptyOutDir: true,
  },
})
```

Basically we are indicating where to build the project and not to show any warning when doing it outside the root directory. We will build both projects in our root directory _**"/react-apps"**_.

In _**"nested-app"**_ our configuration file should look like this:

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  base: '/nested-app/',
  build: {
    outDir: '../dist/nested-app',
    emptyOutDir: true,
  },
})
```
Here are two differences:
- The first is that we are indicating that _**"/nested-app/"**_ will be the public route where the application will serve us both in development and in production.
- The second is that we are telling it to build the project in a subdirectory of the _**"original-app"**_ build.


Once we have the configuration files as we have seen, the next step would be to [install React Router](https://reactrouter.com/en/main/start/tutorial#setup) in both projects.

```
npm install react-router-dom localforage match-sorter sort-by
```

We will need to use the ["HashRouter"](https://reactrouter.com/en/main/router-components/hash-router) component so that the two applications can coexist and we can go from one to the other once they are in our bucket or we are serving the static ones through _**"npm serve"**_ . You can read the official documentation to understand how works this component.

To understand the behavior at a basic level, let's take a look at the _**"App.jsx"**_ components of both the _**"original-app"**_ and _**"nested-app"**_ applications.

In both applications we have configured two routes:
- **_Home page_** => _**"/"**_
- **_Page about_** => _**"/about"**_

This would be the component of our _**"original-app"**_ project:

```react
// Path: react-apps/original-app/App.jsx

import { HashRouter, Route, Routes } from 'react-router-dom'

const App = () => {
    return (
        <HashRouter basename={'/'}>
        <Routes>
          <Route
            path="/"
            element={
              <div>
                <a href="/nested-app/#/about">About Nested App</a>
                <a href="/#/about">About Original App</a>
              </div>
            }
          />
          <Route 
            path="/about/" 
            element={
                <div>
                    <a href="/nested-app/#/">Home Nested App</a>
                    <a href="/#/">Home Original App</a>
                </div>
            } 
          />
        </Routes>
      </HashRouter>
    )
}

export default App
```

And this would be the component of our _**"nested-app"**_ project:

```react
// Path: react-apps/nested-app/App.jsx

import { HashRouter, Route, Routes } from 'react-router-dom'

const App = () => {
    return (
        <HashRouter basename={'/'}>
        <Routes>
          <Route 
            path="/" 
            element={
            <div>
                <a href="/nested-app/#/about">About Nested App</a>
                <a href="/#/about">About Original App</a>
            </div>
            } 
          />
          <Route
            path="/about/"
            element={
              <div>
                <a href="/#/">Home Original App</a>
                <a href="/nested-app/#/">Home Nested App</a>
              </div>
            }
          />
        </Routes>
      </HashRouter>
    )
}

export default App
```

Right now we could move between the two routes of both applications. Jumping from one application to another with the only peculiarity that the **_"HashRouter"_** component works by adding _**"Hash"**_ to the routes. You could share information between the two applications either through the URL or through the _**"SessionStorage"**_ or _**"LocalStorage"**_.

Once we have all this structure we can execute the build of _**"original-app"**_ and then the build of _**"nested-app"**_.

```bash
# Path: react-apps/original-app
npm run build
```
```bash
# Path: react-apps/nested-app
npm run build
```
The _**"/dist"**_ folder will be generated in our root directory and we should see something like this:
```
./react-apps
   /original-app #### Original react app
   /nested-app #### Nested react app
   /dist #### Folder with both builds
```

Inside of _**"/dist"**_ folder:
```
.dist/
   index.html #### Index of original-app
   /assets #### Assets of original-app
   /nested-app #### Folder with nested-app
     index.html #### Index of nested-app
     /assets #### Assets of nested-app
```
Now we could upload the contents of the _**"/dist"**_ folder and have the two applications coexist within the same S3 bucket. We could also serve our statics by running the _**"serve"**_ command from the _**"/original-app"**_ folder which would be our root application.

```bash
# Path: ./react-apps/original-app
npm run serve
```

Finally, just remember that this is not a good practice and that the React Router documentation itself currently advises against the use of the _**["HashRouter"](r)**_ component. But for certain cases, it could be a temporary solution to a problem since with this it would not be necessary to use _**[AWS "CloudFront"](https://docs.aws.amazon.com/es_es/AmazonCloudFront/latest/DeveloperGuide/Introduction.html)**_, just the S3 bucket.

Thanks to my co-worker Víctor :raised_hands: and the [community of Daniel Primo](https://www.webreactiva.com/) :purple_heart: who helped me with this experiment.

Greetings :wave: