DEV Community

Brewhouse Digital
Brewhouse Digital

Posted on

NextJS getStaticProps With Components

Ever needed to fetch some content from your server to populate a global component, only to realize that NextJS doesn't allow for that 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
Enter fullscreen mode Exit fullscreen mode

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

Setting Up A Test

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

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

module.exports.preBuildProduction = async() => {
    console.log("Loading the production content!")
}
Enter fullscreen mode Exit fullscreen mode

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

preBuildDev.js

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

preBuildDevelopment();
Enter fullscreen mode Exit fullscreen mode

preBuildProd.js

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

preBuildProduction();
Enter fullscreen mode Exit fullscreen mode

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:

"scripts": {
    "dev": "next dev",
    "build": "next build"
}
Enter fullscreen mode Exit fullscreen mode

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

Multiple Environment Example

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

Single Environment Example

Here is an example of a single environment setup:

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

    "prebuild": "node src/functions/preBuildScripts/preBuildDev.js",
    "build": "next build"
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

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

Top comments (1)

Collapse
 
tacochen profile image
Wei-Fan Chen

This is a great idea to handle server-side rendering for components. However, I really hope next.is could adapt some kinds of this workaround in the official release.