My approach to organizing next.js applications

Posted by


Next.js is a popular React framework that is commonly used for building complex, dynamic web applications. In this tutorial, I will explain how I typically structure my Next.js applications to ensure maintainability, scalability, and organization.

  1. Organizing folders and files:

First, let’s start by setting up the basic folder structure of our Next.js application. Here is an example of how you can organize your files and folders:

  • pages/ (contains all of your routes and components)
    • index.js
    • about.js
    • contact.js
  • components/ (contains reusable components)
    • Header.js
    • Footer.js
  • styles/ (contains global styles and CSS modules)
    • global.css
    • index.module.css
  • public/ (contains static assets like images and fonts)
    • logo.png
  • utils/ (contains utility functions and helper files)
    • api.js
    • auth.js
  1. Creating pages:

Next.js uses the concept of pages to handle routing. Each file in the pages/ directory represents a route in your application. For example, if you create a file called about.js in the pages/ directory, you can access it at /about in your browser.

Each page file in Next.js should export a React component that represents the content of that page. Here is an example of what a typical page file might look like:

// pages/about.js
import React from 'react';

const AboutPage = () => {
  return (
    <div>
      <h1>About Us</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </div>
  );
};

export default AboutPage;
  1. Creating components:

Next.js allows you to create reusable components that can be shared across multiple pages. These components can be placed in the components/ directory. Here is an example of what a typical component file might look like:

// components/Header.js
import React from 'react';

const Header = () => {
  return (
    <header>
      <h1>My Next.js App</h1>
    </header>
  );
};

export default Header;

You can then import and use this component in your page files like so:

// pages/index.js
import React from 'react';
import Header from '../components/Header';

const HomePage = () => {
  return (
    <div>
      <Header />
      <h1>Welcome to my Next.js App!</h1>
    </div>
  );
};

export default HomePage;
  1. Styling:

Next.js supports both global styles and CSS modules for styling your components. Global styles can be placed in the styles/ directory and imported into your application’s main file (e.g., pages/_app.js). CSS modules can be used to create scoped styles for individual components. Here is an example of how you can use CSS modules in Next.js:

/* styles/index.module.css */
.container {
  max-width: 1200px;
  margin: 0 auto;
}

.title {
  font-size: 24px;
  color: blue;
}
// components/Footer.js
import React from 'react';
import styles from '../styles/index.module.css';

const Footer = () => {
  return (
    <footer className={styles.container}>
      <p className={styles.title}>© 2022 My Next.js App</p>
    </footer>
  );
};

export default Footer;
  1. Using utility functions:

Next.js applications often require helper functions and utility files to handle tasks like fetching data from an API, managing authentication, or handling form submissions. You can place these utility functions in the utils/ directory and import them into your components and pages as needed. Here is an example of how you can create a utility function for fetching data from an API:

// utils/api.js
export const fetchData = async () => {
  try {
    const response = await fetch('https://api.example.com/data');
    return response.json();
  } catch (error) {
    console.error('Error fetching data:', error);
    return null;
  }
};
// pages/index.js
import React, { useEffect, useState } from 'react';
import { fetchData } from '../utils/api';

const HomePage = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData().then((data) => setData(data));
  }, []);

  return (
    <div>
      <h1>Welcome to my Next.js App!</h1>
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  );
};

export default HomePage;

By following this structured approach to organizing your Next.js application, you can ensure that your codebase remains clean, maintainable, and easy to navigate. Additionally, separating your code into distinct folders and files can help improve collaboration among team members and streamline the development process.

0 0 votes
Article Rating
31 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
@KalidAhmed-w5r
2 months ago

A real stuff! Text a lot

@tiagodev5838
2 months ago

Great video! I've been thinking about how I could use clean architecture in NextJS. You even touched upon a few concepts from Domain Driven Design in there. Cheers!

@srenhyer3895
2 months ago

Instead of calling multiple use cases direcly inside your server components you might want to do it from a single controller instead 🙂 But great video.

@jagrat12354
2 months ago

should there also be services after use cases ? Use cases will call diffrent services and the services will call the data access

@Brian-bo2fu
2 months ago

What if i already have a backend service, should i just remove the usecase and repository layer and call the backend api directly?

@radityadito922
2 months ago

Can you give me the example repo

@y8rRMNqiP4mAfHQ7tH6deX
2 months ago

what is your vscode theme name?

@HarisVFX
2 months ago

I understand this abstracts/separates the 3 layers but could you just handle all of your business logic by chaining another zsa procedure say to check the group and pass it as context to the action etc ? I'm guessing this is inconvenient if you want to get group/role for other purposes/areas in the app?

@thanhminh5235
2 months ago

Dislike the vid because you didn't upgrade those extensions and the line wasn't straight

@tyeinstem
2 months ago

I knew something was wrong with how I was coding but didn't know what it was, this video is what I needed – ty!

@alexald783
2 months ago

How do you handle transactions if you have multiple data access calls? Like how do you keep things still de-coupled?

@caseyspaulding
2 months ago

Yea! I think your the only person talking about this topic in Nextjs community

@endlessia
2 months ago

Hii Cody, I like this architecture type, everything gets way cleaner and its kinda looks like a backend architecture with Controller and Services, I love it !

I have one question, inside the data-access & uses-cases files do we need to put « use server » ? Or maybe import server-only ? I thought it was necessary 🤔 (even tho I don’t know the difference between both of them lmao)

@wazzadev7209
2 months ago

A question, say you have a usecase where you'll need to perform a db transaction involving multiple tables, how should this be implemented?

@williamx0
2 months ago

In the past, you would have recommended to do dependency injection on the use-cases or business logic so that it's not dependent on any external libraries (in this case drizzle-orm). Would you say injecting dependencies is just more cumbersome now and you're okay to have your business logic tied to the database repository functions or would you still dependency inject? I'm asking because I'm having the same issue

@DatoDaraselia
2 months ago

Thanks Cody! This is so useful!

@Ahmed_005
2 months ago

16:45 would you also recommend co-locating your unit-tests? So your unit tests would be in the same directory as the function that it is testing.

@LofiChillOut
2 months ago

Great stuff. Can you link to the safe server action package

@Alex.Shalda
2 months ago

awesome

@kryttee
2 months ago

Would love to see github repo of this, it's hard for me to comprehend the structure just by looking at the video