Next.js is a popular framework for building server-rendered React applications. However, when JavaScript modules are shared between server and client components, it can introduce a potential vulnerability. This blog post will explore the risks associated with server-only code leaking into client components and present a solution using the "server-only" package in Next.js. We will learn how to identify and restrict server-specific code from being imported into client components, ensuring code integrity and security.
Understanding the Risk
When developing web applications, it is essential to differentiate between code that should only run on the server and code suitable for client-side execution. Failure to do so can result in the leakage of sensitive information or the execution of code in an unintended context. Let's examine an example to understand this risk:
// lib/data.ts
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
});
return res.json();
}
In the above code snippet, getData()
appears to work on both the server and the client. However, the authorization
header relies on the process.env.API_KEY
, which is a private environment variable accessible only on the server. Next.js automatically replaces private environment variables with an empty string in client code to prevent the leakage of secure information. Consequently, although getData()
can be imported and executed on the client, it won't function as intended. Making the variable public would resolve the issue but would compromise sensitive information.
Meet the "server-only" Package
To prevent the accidental usage of server-only code in client components, Next.js provides the "server-only" package. This package helps developers identify and restrict the usage of server-specific code, ensuring its execution exclusively on the server.
Installation
Install the "server-only" package using the following command:
npm install server-only
Implementation
Now importing the "server-only" package, any client component that attempts to import the server-only module will encounter a build-time error, clearly indicating that the module can only be used on the server.
Example:
// lib/data.ts
import 'server-only';
export async function getData() {
// Code that should only run on the server
}
Bonus π₯
Handling Client-Only Code
Next.js also offers the "client-only" package to handle modules containing client-only code, such as those relying on browser-specific APIs like the window object. Marking modules with "client-only" ensures they are only used on the client-side, preventing unintended access or execution on the server.
Installation
Install the "client-only" package using the following command:
npm install client-only
Implementation
By importing the "client-only" package, any server component that mistakenly imports the client-only module will generate a build-time error, notifying developers that the module can only be used on the client.
// lib/client-only-module.ts
import 'client-only';
// Client-only code utilizing browser-specific APIs
Like what you see? π
Feel free to follow me on Twitter, GitHub, and LinkedIn for more articles and insights on Next.js and other frontend development topics. Let's connect and stay updated on the latest trends and techniques in the world of web development!