Published on

Why being able to use dotenv with Next.js is so huge

Last Modified on
Last modified on
Authors
Why being able to use dotenv with Next.js is so huge
Photo by Kristina Flour on Unsplash

Today I pretty much completed the revamp of this website. My previous build had been with GatsbyJS, but from the beginning, I seemed to constantly encounter issues with it. I had chosen it as a static framework because of the image optimization it provided. I also was interested in GraphQL at the time (still am, but more on the backend), so that is why I went with it.

But these days, there are more options to choose from, and which have more features to offer out of the box.

I had heard about Next.js for a while, and what attracted me to it the most was its server side rendering and the ability to tack on a backend. But what does server side rendering mean?

According to Educative,

Server-side rendering (SSR), is the ability of an application to contribute by displaying the web-page on the server instead of rendering it in the browser. Server-side sends a fully rendered page to the client; the client’s JavaScript bundle takes over and allows the SPA framework to operate. There is also client-side rendering which slows down the procedure of viewing and interacting with the web page. — Educative.io

But why is this such a big deal, you may ask.

There are great advantages to using SSR.

  • Pages load faster, which means better UX.

  • It greatly contributes to Search Engine Optimization (SEO), and correctly indexes pages. Google favors web pages with faster load time.

  • It contributes to the loading of a user's page when they have a slow internet connection.

GatsbyJS also has server side rendering, but not in the same way as Next.JS. It is still a purely static, frontend framework. AND build time was never blazing fast!

With Next.js, there is also the ability to use environment variables, which would be typically used in an application with a backend. Well, we CAN add a backend to our Next.js applications! But we can also use them in our Next.js static sites by adding the NEXT_PUBLIC prefix.

And why is the ability to use environment variables so huge? It means that we can hide our little sensitive secrets from the browser. With purely static, frontend applications, we cannot do that, thereby exposing our sensitive data to the world within our pages' parsed HTML markup.

That's great, you may say. But what is the big deal about being able to use dotenv with Next.js? Doesn't Next.js already have its own way of dealing with environment variables?

YES. It does. However, the process is a bit "circular", and not as simple to implement. Honestly, I did not even try to learn it after reading about it in several places. I thought there had to be an easier way. And since I have used dotenv since learning about it some years ago, I thought there had to be a way of using it in Next.js. And there sure is. Above all, it is SO EASY to implement. Why would I want to configure my environment variables in ANY OTHER WAY?

How did I find out about it? I came across an article on Medium entitled Environmental Variables in Next.js with dotenv, by Deepak Surya, which provided the answer to my question: "How can I use dotenv with Next.js?"

I did make a slight modification to the code Deepak Surya presented in the article, making the process even easier.

The first environment variables I added were for my ConvertKit newsletter form inside the Next.js Tailwindcss starter blog I use(d) to build this site. And the process was the following:

First, I had to specifiy convertkit inside my siteMetadata.js file:

//data/siteMetadata.js
newsletter: {
  // supports mailchimp, buttondown, convertkit, klaviyo, revue, emailoctopus
  // Please add your .env file and modify it according to your selection
  provider: 'convertkit',
},

Next, I added the NEXT_PUBLIC prefix to the ConvertKit related variables I was using in convertkit.js:

//api/convertkit.js
const FORM_ID = process.env.NEXT_PUBLIC_CONVERTKIT_FORM_ID
const API_KEY = process.env.NEXT_PUBLIC_CONVERTKIT_API_KEY
const API_URL = process.env.NEXT_PUBLIC_CONVERTKIT_API_URL

which initially was:

const FORM_ID = process.env.CONVERTKIT_FORM_ID
const API_KEY = process.env.CONVERTKIT_API_KEY
const API_URL = process.env.CONVERTKIT_API_URL

I added the NEXT_PUBLIC prefix to make the environment variables accessible to the browser.

But none of this meant anything until I installed the dotenv npm package in my Next.js application from inside the root of the application. I installed it using the following npm command:

npm i dotenv -S

Then I had to actually use this package somewhere. It is always used in the backend and therefore is required instead of imported into a file. So that meant requiring it inside the next.config.js file where the require keyword can be used. It is not a JavaScript keyword, but a Node.js keyword. It is a built-in Node.js function whith the sole purpose of loading modules such as dotenv! So right above my module.exports in Next.js, which is also used in Node.js instead of JavaScript's export default or export const, for example, I added the following:

const webpack = require('webpack')

const dotenv = require('dotenv')

dotenv.config()

It is essential to require webpack, as done above. Otherwise, an error will be thrown. Something like the following:

npm run dev

> tailwind-nextjs-starter-blog@1.5.5 dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info  - Loaded env from /Users/mariacam/Development/interglobalmedia-tailwind-nextjs/.env.local
info  - Loaded env from /Users/mariacam/Development/interglobalmedia-tailwind-nextjs/.env
info  - Disabled SWC as replacement for Babel because of custom Babel configuration ".babelrc" https://nextjs.org/docs/messages/swc-disabled
info  - `compiler` options in `next.config.js` will be ignored while using Babel https://nextjs.org/docs/messages/ignored-compiler-options
ReferenceError: webpack is not defined
at Object.webpack (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/next.config.js:90:11)
at Object.webpack (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/@next/bundle-analyzer/index.js:19:29)
at Object.getBaseWebpackConfig [as default] (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/build/webpack-config.js:1176:32)
at async Promise.all (index 0)
at async Span.traceAsyncFn (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/trace/trace.js:79:20)
at async Span.traceAsyncFn (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/trace/trace.js:79:20)
at async HotReloader.start (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/server/dev/hot-reloader.js:337:25)
at async DevServer.prepare (/Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/server/dev/next-dev-server.js:292:9)
at async /Users/mariacam/Development/interglobalmedia-tailwind-nextjs/node_modules/next/dist/cli/next-dev.js:127:9

It is necessary to make sure it is explicitly required in the file, because process.env is not available in browsers, but as already mentioned, Next.js both runs in the server as well as the browser.

Then I had to require dotenv. THEN, instead of what Deepak states, all we have to do is add dotenv.config(). I would do the same if working solely in a Node.js application, and was not requiring dotenv in a file located in the root of the application, but inside another subfolder. That is all you have to do to make it work anywhere. And since I had placed my .env file in my application's root folder, I did not have to set the path to my .env file. In fact, when working with dotenv, you always have to place the .env file inside the root folder of your application.

Next, inside my module.exports, I had to add the following:

//next.config.js
config.plugins.push(
	new webpack.EnvironmentPlugin(
		'NEXT_PUBLIC_CONVERTKIT_API_URL',
		'NEXT_PUBLIC_CONVERTKIT_API_KEY',
		'NEXT_PUBLIC_CONVERTKIT_FORM_ID',
	),
)

I already was using webpack inside next.config.js, so I did not need to include webpack(config) {}. webpack was already there. So I placed the above code right before the return config statement, which was also already there. I also included environment variables for Giscus Github comments, and Google Analytics. The process is the same. And that is it!

As Deepak mentions in her article, the webpack.EnvironmentPlugin() "parses" the destructured object she includes in her code. However, that is not a good demonstration of what actually should be done. The above is the correct approach. Please refer to the webpack documentation regarding the webpack.EnvironmentPlugin(): (webpack) EnvironmentPlugin.

And THAT is IT!

As I mention in my Tweet on July 4, 2022,

@nextjs is a good reason why one ever again should be revealing sensitive data on client side. No excuse. And I am seeing it out there when using Nextjs. No no no!

Maria D. Campbell (@letsbsocial1) - July 4, 2022

Happy dotenving!