NextJS getStaticProps With Components
Tags: javascript, nextjs
Published: 2022-07-20 14:55:37

Ever needed to fetch some content from your server to populate a global component, only to realize that NextJS [doesn't allow for that](https://nextjs.org/docs/basic-features/data-fetching/get-static-props#where-can-i-use-getstaticprops) out of the box? A project I worked on had a global navigation bar that was imported on 202 different pages, but needed pre-generated static content that came from a CMS. Instead of using `getStaticProps` on every single page, I decided to dive in and see if I could pre-generate all that content a single time, and import it directly into the component.

The way the team decided to get around this `getStaticProps` page restriction is to use the NodeJS build process to fetch content from wherever we need, and put it into the project for our components to import as standard `json` files. We can even do different versions based on dev and production environments.

## Setup

Find a nice place in your project directory to add in your `preBuildScripts` folder (you can name this whatever you want). In my project directory I have:

```
src -> functions -> preBuildScripts
```

If you have a single environment that you deploy to, you'll only need two files. For each additional environment, you'll need an additional file to run. In this example, we'll be building files for a Development environment, and a Production environment.

New Files:

```
preBuildUtilities.js
preBuildDev.js
preBuildProd.js
```

## Setting Up A Test

Inside your `preBuildUtilities.js`, add in this module exports code that looks like this:

```js
module.exports.preBuildDevelopment = async() => {
    console.log("Loading the development content!")
}

module.exports.preBuildProduction = async() => {
	console.log("Loading the production content!")
}
```

Now inside each of your other files, we can call the respective functions

`preBuildDev.js`

```js
const {preBuildDevelopment} = require("./preBuildUtilities.js")

preBuildDevelopment();
```

`preBuildProd.js`

```js
const {preBuildProduction} = require("./preBuildUtilities.js")

preBuildProduction();
```

And now our setup is pretty much done! The way we call these functions is simple. NPM automatically runs scripts in the `package.json` file that have the prefix of `pre`. You can read more about that here: https://docs.npmjs.com/cli/v8/using-npm/scripts#life-cycle-scripts

## Updating Your Package.json File

Open up your `package.json` and find your `scripts` section that shows:

```json
"scripts": {
	"dev": "next dev",
	"build": "next build"
}
```

and add in a new script above that one like so:

### Multiple Environment Example

```json
"scripts": {
    "predev": "node src/functions/preBuildScripts/preBuildDev.js",
    "dev": "next dev",

    "prebuild:dev": "node src/functions/preBuildScripts/preBuildDev.js",
    "build:dev": "next build",

    "prebuild:prod": "node src/functions/preBuildScripts/preBuildProd.js",
    "build:prod": "next build"
}
```

### Single Environment Example

Here is an example of a single environment setup:

```json
"scripts": {
    "predev": "node src/functions/preBuildScripts/preBuildDev.js",
    "dev": "next dev",

    "prebuild": "node src/functions/preBuildScripts/preBuildDev.js",
    "build": "next build"
}
```

The folder path needs to be modified to fit your specific project. Now make sure your server is off, open up your terminal and run your normal command `npm run dev`. You should see `Loading the development content!` show up in your console!

Now that we have the setup done, we can add whatever functions we want into our `preBuildUtilities.js`

## Creating New Files

Inside `preBuildUtilities.js`, let's create a sample API request that will generate some JSON data for us. Pretend like we're pulling in content from a CMS and want to populate a component with the data.

We can use the [Star Wars API](https://swapi.dev/) for this, and just replace this functionality with your own needs. We'll just be focusing on the `preBuildDevelopment()` function, but it will follow the same pattern for `preBuildProduction()`.

Here is our steps to build our preBuild script:

1. Import the NodeJS File System package
2. Setup our API using async/await
3. Create a new file in the project using the JSON data

We'll be using Axios in this example since Fetch isn't widely supported in NodeJS yet. Feel free to use any kind of HTTP request system that your project needs. It will work the same.

Note: We'll be using synchronous here to prevent any race conditions. Also make sure that the folder exists before running this or Node may error out trying to find the proper directory.

```js
const fs = require('fs');
const axios = require('axios').default;

module.exports.preBuildDevelopment = async() => {
    const API = `https://swapi.dev/api/people`;

    const response = await axios.get(API);
    const data = response.data.results;

    fs.writeFileSync("./src/data/preBuild/starWarsData.json", JSON.stringify(data))
}
```

Make sure your server is turned off, and then launch it using `npm run dev`. Once the server is up and running, go check your file directory to see if your new JSON file is there. Hopefully it is!

Now in any of your components, you can reference the data like so:

`StarWarsList.js`

```jsx
import starWarsData from "src/data/preBuild/starWarsData.json";

export const StarWarsList = () => {
    return (
        <ul>
			{starWarsData.map((item, index) => (
				<li key={`star-wars-item-${index}`}>{item.name}</li>
			))}
		</ul>
	)
}
```

Now your component is utilizing server-side/static data! 🎉