Managing Multiple Gatsby Projects in a Monorepo - A Unified Build Strategy

Managing Multiple Gatsby Projects in a Monorepo - A Unified Build Strategy

In modern web development, projects often share components, styles, or even parts of a website between each other. So, maintaining these projects efficiently, especially when they are supposed to work with each other, could be rather cumbersome. That’s where the monorepo shines.

In this article, we’re going to discuss how to set up a monorepo with two Gatsby projects and one shared component library. We’re going to go over one unique way of deploying where we copy the build files from one project into another, allowing them to run under one single localhost port.

Why Use a Monorepo?

Monorepo allows multiple projects to coexist in a single repository, offering easy sharing of code, uniformity in tooling, and management of dependencies. That would be great in situations where you have related projects, which would share common components or that needed to be deployed together.

Setting Up the Monorepo

  1. Initial Setup

    First, let’s create a monorepo structure using npm. If you haven’t already, set up npm workspaces in your project.

     
               
                
                  mkdir my-monorepo
                  cd my-monorepo
                  npm init -y
                 
               
            

    Now, configure npm workspaces in your package.json

     
               
                
                {
                  "name": "my-monorepo",
                  "version": "1.0.0",
                  "private": true,
                  "scripts": {
                    "build": "npm run build:project-one && npm run build:project-two",
                    "build:project-one": "npm run --workspace=packages/project-one build",
                    "build:project-two": "npm run --workspace=packages/project-two build",
                    "build:shared-components": "npm run --workspace=packages/shared-components build",
                    "develop": "npm run develop:project-one && npm run develop:project-two",
                    "develop:project-one": "npm run --workspace=packages/project-one develop",
                    "develop:project-two": "npm run --workspace=packages/project-two develop",
                    "clean": "npm run clean:project-one && npm run clean:project-two",
                    "clean:project-one": "npm run --workspace=packages/project-one clean",
                    "clean:project-two": "npm run --workspace=packages/project-two clean",
                    "clean:shared-components": "npm run --workspace=packages/shared-components clean"
                  },
                  "keywords": [],
                  "workspaces": [
                    "packages/project-one",
                    "packages/project-two",
                  ]
                }
                 
               
            
  2. Creating the Projects

    Next, we’ll create our two Gatsby projects and a shared component library inside the packages directory

     
               
                
                  mkdir packages
                  cd packages
    
                  npx gatsby new project-one
                  npx gatsby new project-two
    
                  mkdir shared-components
                  cd shared-components
                  npm init -y
                 
               
            
  3. Setting Up the Shared Component Library

    Next, we’ll create a shared-components package to store reusable components that can be used across multiple Gatsby projects.


    1. Creating an index.js File in shared-components

      In the shared-components package, create an index.js file that exports all the components

       
                       
                        
                          // packages/shared-components/src/index.js
                          export { default as Button } from './Button';
                          export { default as Header } from './Header';
                          // Add other components as needed
                         
                       
                    

      This allows you to import all components from the shared-components package using a single import statement in your other projects.

    2. Creating an Button.js File (Simple React component) in shared-components

      In the shared-components package, create a simple React component

       
                       
                        
                          mkdir src
                          fsutil file createnew Button.js 0
                         
                       
                    

      Add the following code to Button.js

       
                       
                        
                          import React from 'react';
      
                          const Button = ({ children, onClick }) => {
                            return (
                              <button onClick={onClick} style={{ padding: '10px', backgroundColor: '#007bff', color: '#fff', border: 'none', borderRadius: '5px' }}>{children}</button>
                          );
                          };
                          export default Button;
                         
                       
                    
  4. Integrating the Shared Components into Gatsby Projects

    To use the shared components in our Gatsby projects, we’ll need to install the shared-components package in both projects

     
               
                
                  cd ../project-one
                  npm install ../shared-components
    
                  cd ../project-two
                  npm install ../shared-components
                 
               
            

    Or, You can manully add the shared-components package as to the both projects as follows

    Gatsby project-one’s package.json

     
               
                
                  {
                    "name": "project-one",
                    "version": "1.0.0",
                    "private": true,
                    "description": "Gatsby project-one",
                    "author": "",
                    "scripts": {
                      "develop": "gatsby develop",
                      "start": "gatsby develop",
                      "build": "gatsby build",
                      "serve": "gatsby serve",
                      "clean": "gatsby clean"
                    },
                    "dependencies": {
                      "@emotion/react": "^11.11.4",
                      "@emotion/styled": "^11.11.0",
                      "@mui/icons-material": "^5.15.11",
                      "@mui/lab": "^5.0.0-alpha.166",
                      "@mui/material": "^5.15.11",
                      "@resuwords/shared-components": "*",  // Install shared components here
                      "axios": "^1.6.7",
                      "gatsby": "^5.13.3",
                      "react": "^18.2.0",
                      "react-dom": "^18.2.0"
                    }
                  }
                 
               
            

    Now, you can use the shared Button component in both Gatsby projects. For example, in project-one

     
               
                
                  import React from 'react';
                  import Button from 'shared-components';
    
                  const IndexPage = () => (
                    <main>
                      <h1>Welcome to Project One</h1>
                      <Button onClick={() => alert('Button clicked!')}>Click Me</Button>
                    </main>
                  );
    
                  export default IndexPage;
                 
               
            

    Repeat the same process in project-two.

  5. Building and Copying Build Files

    Now that we have our projects set up, let’s configure the build and deployment process.

    1. Building the Projects

      Run the build command for both Gatsby projects

       
                       
                         
                          cd packages/project-one
                          npm run build
      
                          cd ../project-two
                          npm run build
                         
                       
                    
    2. Copying build files of the project-two to the project-one

      In the, project-two’s package.json file’s build script should be like below.

       
                       
                         
                          {
                            "name": "project-two",
                            "version": "1.0.0",
                            "private": true,
                            "description": "Gatsby project-two",
                            "author": "",
                            "scripts": {
                              "develop": "gatsby develop",
                              "start": "gatsby develop",
                              "build": "gatsby build --prefix-paths && xcopy public\\* ..\\project-one\\public\\project-two /E /I /Y",
                              "serve": "gatsby serve",
                              "clean": "gatsby clean"
                            },
                          "dependencies": {
                            "@emotion/react": "^11.11.4",
                            "@emotion/styled": "^11.11.0",
                            "@mui/icons-material": "^5.15.11",
                            "@mui/lab": "^5.0.0-alpha.166",
                            "@mui/material": "^5.15.11",
                            "@resuwords/shared-components": "*",  // Install shared components here
                            "axios": "^1.6.7",
                            "gatsby": "^5.13.3",
                            "react": "^18.2.0",
                            "react-dom": "^18.2.0"
                          }
                        }
                         
                       
                    

      The build script provided is designed to work on Windows using the xcopy command. Here's how it works and how you can achieve the same result on Ubuntu or other Unix-like operating systems.


      Windows (Using xcopy)
       
                       
                         
                          "build": "gatsby build --prefix-paths && xcopy public\\* ..\\project-one\\public\\project-two /E /I /Y"
                         
                       
                    

      xcopy is a command-line utility in Windows that allows copying files and directories, including subdirectories and hidden files.

      The xcopy public\\* ..\\resuwords-web\\public\\project-two /E /I /Y part of the script.

      • /E: Copies all subdirectories, including empty ones.
      • /I: Assumes that the destination is a directory.
      • /Y: Suppresses confirmation prompts to overwrite files.

      This setup works perfectly on Windows environments, but xcopy is not available on Ubuntu or other Unix-like systems.


    Ubuntu (Using cp)

    To achieve the same functionality on Ubuntu, you would replace xcopy with the cp command, which is used to copy files and directories.

    Here’s how you can modify the build script for Ubuntu

     
               
                 
                  "build": "gatsby build --prefix-paths && cp -r public/* ../project-one/public/project-two"
                 
               
            
    cp -r public/* ../resuwords-web/public/project-two
    • cp is the Unix command to copy files and directories.
    • -r (or --recursive) tells cp to copy directories recursively, including all files and subdirectories.
    • public/* selects all files and subdirectories in the public directory.
    • ../project-one/public/project-two is the destination where the files should be copied.
  6. Building the Projects

    With everything set up, you can now build your projects. Run the build script to build both project-one and project-two. The build files of project-two will be copied into the project-one project to be served under the same localhost port.

     
               
                 
                  npm run build
                 
               
            
  7. Serving Both Projects Under One Localhost Port

    After copying project-two’s build files to the project-one’s public folder, to serve both projects under a single localhost port, we’ll use the gatsby serve command for project-one

    You can now access both projects by navigating to.

    • http://localhost:8000/ for project-one
    • http://localhost:8000/project-two/ for project-two

    This approach allows you to run both projects under one port, creating a seamless user experience and simplifying local development.

Conclusion

In this blog post, we have seen how to configure a monorepo with two Gatsby projects and a shared component library using npm. By copying one project’s build files into another, we are able to serve both projects under one localhost port. It is a specially useful technique in projects which need to interact closely with one another but still maintain a modular, scalable architecture.

Having a monorepo can yield great benefits whether you are building a multi-site platform or just want to ease your development life. If you’re working on similar setups or have questions-please share your thoughts in comments!