Next.js is a React framework used for building full-stack web applications, supporting both server-side and client-side features. The server-side handles application logic, including routing, data fetching, and database connections, while the client-side uses React components to manage the user interface.
Sometimes we might want to create a reusable component that can be shared between React components and Next.js components. This can generate import errors if the shared component contains client-side hooks such as useState, useReducer, and useEffect, which are not supported in server-side Next.js code.
In this tutorial, we’ll learn how to use Rehackt to import React components into server-side code without causing build failures due to client-side hooks.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
In Next.js, components are server-side by default. Developers must explicitly mark a component as a Client Component to use event handlers and client-side hooks like useState, useContext, and useEffect. Attempting to import React components with client-side hooks into server-side code will result in build errors in production.
Let’s take a look at the component below which contains code shared between client-side and server-side components:
// shared-code.js
import { useState } from "react";
export const useLoginForm = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleUsernameChange = (e) => setUsername(e.target.value);
const handlePasswordChange = (e) => setPassword(e.target.value);
const validateForm = () => {
if (username.trim() === "" || password.trim() === "") {
setError("Both fields are required.");
return false;
}
setError("");
return true;
};
return {
username,
password,
error,
handleUsernameChange,
handlePasswordChange,
validateForm,
};
};
export const fetchUserData = async (username, password) => {
const users = [
{ username: "popoola", fullname: "Popoola tope", password: "password1" },
{
username: "adewale123",
fullname: "Adewale blessing",
password: "password2",
},
{ username: "joy101", fullname: "Smith joy", password: "password3" },
];
const user = users.find(
(user) => user.username == username && user.password == password
);
if (user) {
return {
success: true,
user,
};
} else {
return {
success: false,
message: "Invalid username or password",
};
}
};
In the shared-code component above:
useLoginForm custom hook manages the state and behavior of a login form using the React client-side useState hook, which is not supported on the server sidefetchUserData function is server-side code that validates the user’s login details and returns the details of the logged-in userWe can easily utilize the above component in the client-side component, as shown in the
LoginForm component below:
// src/app/components/LoginForm.js
"use client";
import { useFormState } from "react-dom";
import { useLoginForm } from "./shared-code";
import someAction from "./action";
export const LoginForm = () => {
const [data, action] = useFormState(someAction, "Hello client");
const {
username,
password,
error,
handleUsernameChange,
handlePasswordChange,
validateForm,
} = useLoginForm();
const handleSubmit = (event) => {
event.preventDefault();
if (validateForm()) {
action({ username, password });
}
};
return (
<form onSubmit={handleSubmit}>
<p>{data}</p>
{error && <p style={{ color: "red" }}>{error}</p>}
<div>
<label>
Username:
<input
type="text"
value={username}
name="username"
onChange={handleUsernameChange}
/>
</label>
</div>
<div>
<label>
Password:
<input
type="password"
value={password}
name="password"
onChange={handlePasswordChange}
/>
</label>
</div>
<button type="submit">Login</button>
</form>
);
};
export default LoginForm;
In the LoginForm component above:
"use client" at the top of the file
useLoginForm hook is imported from the shared-code component to manage the login form’s state and behaviorLoginForm function utilizes the handleUsernameChange, handlePasswordChange, and validateForm methods from the shared-code component to validate the login details before sending them to the server sideNow, let’s import the fetchUserData function from the shared-code component on the server side to retrieve the details of the logged-in user:
import { fetchUserData } from "./shared-code";
export default async function someAction(username, password) {
const result = await fetchUserData(username, password);
if (result.success) {
return `Hello ${result.user.fullname}, you are logged in.`;
} else {
return result.message;
}
}
The server component above returns the details of the logged-in user to the user interface upon successful authentication if the username and password are correct.
Now, let’s build the application to test if everything is working correctly. You can do this by running the build command below:
npm run build
During the build process, the application will encounter the following build errors and fail.
Failed to compile:
./src/app/components/shared-code.js
Error:
x You are importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so theyre Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
|
|
,-[/Users/popoolatopzy/Desktop/works/logrocket/my-login-app/src/app/components/shared-code.js:1:1]
1 | // shared-code.js
2 | import { useState } from "react";
: ^^^^^^^^
3 |
4 | export const useLoginForm = () => {
5 | const [username, setUsername] = useState("");
`----
Import trace for requested module:
./src/app/components/shared-code.js
./src/app/components/action.js
> Build failed because of webpack errors
This error occurs because the shared-code component uses a useState hook, which is a client-side hook that is not supported in server-side code.
The Rehackt library allows developers to share reusable components between React components and Next.js server components without causing errors. The library achieves this by invisibly wrapping React, enabling the use of React functions like useState, useReducer, and useEffect in shared code that can be used on both the server and client sides of a Next.js application.
To use Rehackt in your application’s component, you need to install the library by running the installation command below:
npm i rehackt
After installing the library, you can use it by importing the React client-side hook from Rehackt, as shown in the code below:
import { useState, useEffect, useReducer } from "rehackt";
When the above code is used in a component shared between the React client side and the Next.js server side, it will prevent the application from throwing errors during build time.
useState cannot be executed on the server, only their import errors are handled.shared-code componentIn our previous code, the shared-code component threw an error because it used React’s useState, which is a client-side hook not supported in Next.js server-side code. To resolve this issue and allow the shared-code component to be shared between React components and Next.js server-side code, you need to import useState from Rehackt by updating shared-code.js with the following code:
// shared-code.js
import { useState } from "rehackt";
export const useLoginForm = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleUsernameChange = (e) => setUsername(e.target.value);
const handlePasswordChange = (e) => setPassword(e.target.value);
const validateForm = (e) => {
if (username.trim() === "" || password.trim() === "") {
setError("Both fields are required.");
return false;
}
setError("");
return true;
};
return {
username,
password,
error,
handleUsernameChange,
handlePasswordChange,
validateForm,
};
};
export const fetchUserData = async (username, password) => {
const users = [
{ username: "popoola", fullname: "Popoola tope", password: "password1" },
{
username: "adewale123",
fullname: "Adewale blessing",
password: "password2",
},
{ username: "joy101", fullname: "Smith joy", password: "password3" },
];
const user = users.find(
(user) => user.username == username && user.password == password
);
if (user) {
return {
success: true,
user,
};
} else {
return {
success: false,
message: "Invalid username or password",
};
}
};
Now, run the build command again. The application should build successfully without any errors related to importing React components in Next.js server-side code, as shown in the build screenshot below:

Rehackt is useful for building complex web applications that involve the use of reusable components, breaking the application’s functions and logic into smaller units that can be shared between React components and Next.js components. This modular approach promotes code consistency, maintainability, and efficiency, making it easier to manage and scale applications.
While Rehackt does not enable the use of client-side hooks in purely server-side code, it effectively removes import errors and facilitates the sharing of client-side logic in a Next.js environment. This capability is particularly valuable for developers looking to build complex, full-stack applications with React and Next.js, as it allows for seamless integration and code reuse across different parts of the application.
In this tutorial, you learned how to create a reusable component that can be shared between React components and Next.js server-side code without causing import errors during build time using Rehackt.
Rehackt provides a robust solution for sharing components that utilize client-side hooks like useState, useReducer, and useEffect between the client and server sides of a Next.js application. This approach allows developers to create reusable components that are accessible to both React components and Next.js server-side code.
Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.
LogRocket captures console logs, errors, network requests, and pixel-perfect DOM recordings from user sessions and lets you replay them as users saw it, eliminating guesswork around why bugs happen — compatible with all frameworks.
LogRocket's Galileo AI watches sessions for you, instantly identifying and explaining user struggles with automated monitoring of your entire product experience.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your Next.js apps — start monitoring for free.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

line-clamp to trim lines of textMaster the CSS line-clamp property. Learn how to truncate text lines, ensure cross-browser compatibility, and avoid hidden UX pitfalls when designing modern web layouts.

Discover seven custom React Hooks that will simplify your web development process and make you a faster, better, more efficient developer.

Promise.all still relevant in 2025?In 2025, async JavaScript looks very different. With tools like Promise.any, Promise.allSettled, and Array.fromAsync, many developers wonder if Promise.all is still worth it. The short answer is yes — but only if you know when and why to use it.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the October 29th issue.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now