DEV Community

Cover image for Two React apps in the same S3 Bucket.
Jorge
Jorge

Posted on • Edited on

Two React apps in the same S3 Bucket.

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
Enter fullscreen mode Exit fullscreen mode

For both projects ( original-app and nested-app) we will use Vite + React. 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:

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,
  },
})
Enter fullscreen mode Exit fullscreen mode

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:

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,
  },
})
Enter fullscreen mode Exit fullscreen mode

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 in both projects.

npm install react-router-dom localforage match-sorter sort-by
Enter fullscreen mode Exit fullscreen mode

We will need to use the "HashRouter" 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:

// 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
Enter fullscreen mode Exit fullscreen mode

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

// 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
Enter fullscreen mode Exit fullscreen mode

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".

# Path: react-apps/original-app
npm run build
Enter fullscreen mode Exit fullscreen mode
# Path: react-apps/nested-app
npm run build
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

# Path: ./react-apps/original-app
npm run serve
Enter fullscreen mode Exit fullscreen mode

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" 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", just the S3 bucket.

Thanks to my co-worker Víctor 🙌 and the community of Daniel Primo 💜 who helped me with this experiment.

Greetings 👋

Top comments (1)

Collapse
 
delineas profile image
Daniel Primo

Great tutorial, congrats!