Modular Next.js Folder Strategy
Tags: nextjs, cleancode, react, designpatterns
Published: 2024-06-23 00:10:18

## Intro 
Organizing your structure folder in Next.js is key to maintain a scalable and maintenable project, today i am gonna share one of the approach i use with Next.js page router.

## ⚙️ Part 0 - Next.js Configuration

This article use a custom folder that require you to change Next.js configuration, modify this line in the `next.config.mjs` file

```js
const nextConfig = {
    pageExtensions: ["page.tsx"],
    ...
}
```

## 🔄 Part 1 - Reusable vs Dedicated Components

The first thing we want to do is divide our project between generic Reusable component and dedicated components.
A reusable component is a component that can be used anywhere (for example the `ReloadButton.tsx`) while a dedicated component is a component that is relative so a specific page (for example the `AboutMeHeader.tsx` or the `AboutMeBody.tsx`)

```
├── components/
│   └── ReloadButton/
│       └── ReloadButton.tsx
└── pages/
    └── about-me/
        ├── components/
        │   ├── AboutMeHeader/
        │   │   └── AboutMeHeader.tsx
        │   └── AboutMeBody/
        │       └── AboutMeBody.tsx
        └── index.png
```

## 🧩 Part 2 - Component Structure Breakdown

For a better structure we want to divide a component into 5 parts: main, logic, style, configuration and types.

Let's see an example for a button that handle async requests

    ReloadButton/
        ├── ReloadButton.tsx
        ├── ReloadButton.style.tsx
        ├── ReloadButton.conf.tsx
        ├── ReloadButton.d.tsx
        └── useReloadButton.tsx

### Main - `ReloadButton.tsx`

This main component serve as a connector for all the other components.

```js
import React from "react";
import { Button } from "./ReloadButton.style";
import useReloadButton from "./useReloadButton";
import { ReloadButtonProps } from "./ReloadButton.d";
import { buttonConfig } from "./ReloadButton.conf.tsx";

const ReloadButton = ({ message, onClick }: ReloadButtonProps) => {
  const { isReloading, handleReload } = useReloadButton({ onClick });

  return (
    <Button onClick={handleReload} disabled={isReloading}>
      {isReloading ? buttonConfig.loadingText : message}
    </Button>
  );
};
```

### Style - `ReloadButton.style.tsx`

The style component isolate the style of this component, it can contains the style of child components in order to isolate all the style of a single component in one single file.
We can expose some variable to ReloadButton.conf in order for them to be more easy to be configurated

```js
import styled from "styled-components";
import { buttonConfig } from "./ReloadButton.conf";

export const Button = styled.button`
  padding: 10px 20px;
  background-color: ${buttonConfig.backgroundColor};
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;

  &:hover {
    background-color: ${buttonConfig.hoverColor};
  }
`;
```

### Logic - `useReloadButton.tsx`

This logic component contain all the states and function of the ReloadButton

While it's possible for the useReloadButton to contain also the logic of child component it's important to note that if u have a useState inside your useReloadButton there will be two different state (one for each component with the useReloadButton), consider solutions with Context or Redux for sharing customHooks between components.

```js
import { useState } from "react";
import { UseReloadButtonProps } from "./ReloadButton.d";

const useReloadButton = ({ onClick }: UseReloadButtonProps) => {
  const [isReloading, setIsReloading] = useState(false);

  const handleReload = async () => {
    setIsReloading(true);
    await onClick();
    setIsReloading(false);
  };

  return {
    isReloading,
    handleReload,
  };
};

export default useReloadButton;
```

### Type - `ReloadButton.d.tsx`

Type is used to store all the types of the component

```js
export interface ReloadButtonProps {
  message: string;
  onClick: () => Promise<void>;
}

export interface UseReloadButtonProps {
  onClick: () => Promise<void>;
}
```

### Config - `ReloadButton.conf.tsx`

Config is used to store all the constants and static configurations of the component

```js
export const buttonConfig = {
  loadingText: "Reloading...",
  backgroundColor: "#0070f3",
  hoverColor: "#005bb5",
};
```

## 💡 Part 3 - Example of usage

```js
import React from "react";
import ReloadButton from "@/components/ReloadButton";

const App = () => {
  const handleReloadClick = async () => {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    console.log("API call completed");
  };

  return (
    <div>
      <h1>Example App</h1>
      <ReloadButton message="Reload Data" onClick={handleReloadClick} />
    </div>
  );
};

export default App;
```

## 📌 Part 4 - Conclusion

While this component could be considered overengineered, it's important to understand the structure in order to avoid having 2000 line files in bigger components!

One of the benefits of this solutions is improved search ,just search for your component name in vscode using CRTL + P or search for something like .style to search for all the styles around your application.

Thank you for reading this ,this was my first article and i hope you enjoyed :D