What Is the Standard Next.js Project Directory Structure?
A standard Next.js project directory structure is not a single correct folder tree that applies identically to every team. Instead, it means following defined conventions for routing files that the framework must interpret, while placing the rest of the code according to the nature of the project. For a new project, an easy-to-understand approach is usually to make src/app the center for URLs and page entry points, separate UI used in multiple places into components, put feature-specific logic in features when needed, and keep shared utilities in lib. The App Router is the primary routing approach in current Next.js, while the Pages Router continues to be supported. nextjs.orgnextjs.org
When defining a structure for the first time, the important thing is not to create as many folders as possible. First distinguish which files create URLs, which handle layouts, error screens, and APIs, and which code is used only within a particular screen. This article examines a maintainable App Router-centered structure based on those distinctions.
Why Is There No “Single Standard Structure” in Next.js?
Next.js provides conventions for building routes from files and folders, but it does not prescribe exactly which folders must contain every component and piece of business logic. In the App Router in particular, folders represent URL segments, and special files such as page.tsx and route.ts create actual public entry points. In contrast, ordinary files placed inside a route do not become external routes by default. nextjs.org
Because of this characteristic, structures can differ even between Next.js applications. A small marketing site may need only app and a few shared components. On the other hand, in a service with multiple domains such as dashboards, orders, and accounts, separating feature-specific code from shared code makes it easier to understand the scope of changes. This is not an absolute rule required by Next.js, but rather an approach to code organization that a team chooses on top of the routing conventions.
Therefore, it is less confusing to think of “standard” in the following two layers.
| Category | Nature | Typical examples |
|---|---|---|
| Conventions interpreted by Next.js | File names and locations directly affect behavior | app/page.tsx, app/layout.tsx, app/api/users/route.ts |
| Conventions defined by the project | The team defines names and boundaries for its purposes | components, features, lib, hooks, types |
Changing the first layer arbitrarily can alter routing or special UI behavior. The second layer can be omitted or combined depending on scale and domain complexity. For example, a structure without features is possible, and there is no need to over-segment every piece of code in a small project.
Why Should New Projects Be Designed Around the App Router?
The App Router is an approach for building pages and layouts based on the app directory. The official documentation recommends moving from the Pages Router to the App Router to take advantage of the latest React features, while the Pages Router itself remains supported. Therefore, you should understand pages conventions when maintaining or learning an existing project structure, but it is natural to treat the App Router as the default candidate when designing a new structure. nextjs.org
The key in the App Router is connecting the URL hierarchy to the file hierarchy. For example, app/dashboard/page.tsx becomes the entry point for the /dashboard page, while app/dashboard/layout.tsx below it can provide a layout applied to dashboard subroutes. The root app/layout.tsx is the root layout that wraps all routes. nextjs.org
This approach is well suited to keeping screen-level code nearby. Tabs, tables, and filter UI needed only by /dashboard can live under app/dashboard, while buttons or input fields reused across multiple screens can move into an external shared folder. The key criterion is not “which technology was used to write this file,” but “within what scope is it reused?”
However, using the App Router does not mean all code must go inside app. You can treat app as a boundary that makes URLs and special files easy to read, and separate shared code or complex feature implementations into other folders as needed. This separation is not performed automatically by Next.js; it is a structural convention the team defines consistently.
When Should You Use a src Folder, and What Stays at the Root?
src is optional. When you use it, you can gather app and application source code under src, visually separating configuration files from runtime code. In contrast, public, package.json, next.config.js, tsconfig.json, and .env.* files belong at the project root. nextjs.org
The following is one easy-to-understand example when using the App Router together with src.
my-app/
├─ public/
│ ├─ images/
│ └─ fonts/
├─ src/
│ ├─ app/
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ ├─ globals.css
│ │ ├─ (marketing)/
│ │ │ └─ about/
│ │ │ └─ page.tsx
│ │ ├─ dashboard/
│ │ │ ├─ layout.tsx
│ │ │ ├─ page.tsx
│ │ │ ├─ loading.tsx
│ │ │ ├─ error.tsx
│ │ │ └─ _components/
│ │ └─ api/
│ │ └─ users/
│ │ └─ route.ts
│ ├─ components/
│ ├─ features/
│ ├─ lib/
│ ├─ hooks/
│ └─ types/
├─ .env.local
├─ next.config.js
├─ package.json
└─ tsconfig.json
In this example, src is only a boundary for application code, not a required mechanism that changes behavior. If an existing project already has app at the root, following the established convention consistently may be better than forcing the addition of src. In particular, if directories named app or pages exist in both the root and src, the root directory takes precedence, so it is important not to leave duplicate structures in place for an extended period during migration. nextjs.org
The practical criterion for choosing src is simple. It is useful if you want to clearly separate configuration files from product code or expect the number of source files to grow. Conversely, there is no need to adopt it in a learning project or a very small project whose root structure is already clear.
Which Files in the app Folder Create Actual Routes?
In the App Router, folders represent pieces of a URL, or segments. However, creating only an app/dashboard folder does not make /dashboard a public page. If that folder contains page.tsx, it becomes a route that provides page UI; if it contains route.ts, it becomes an API endpoint based on a Route Handler. nextjs.org
For example, consider the following layout.
src/app/
├─ page.tsx
├─ about/
│ └─ page.tsx
├─ dashboard/
│ ├─ page.tsx
│ └─ reports/
│ └─ page.tsx
└─ api/
└─ users/
└─ route.ts
In this case, page.tsx corresponds to /, /about, /dashboard, and /dashboard/reports, respectively. api/users/route.ts is not a UI page; it defines an API endpoint. Once you understand that page.tsx and route.ts serve as the public entry points of a route, it becomes clear why other files can live in the same folder. nextjs.org
This property can be viewed as colocation. Colocation is an organizational approach that keeps related code close together. For example, table components used only in dashboard, screen-specific formatting functions, and test-data transformation code can be placed near app/dashboard. This does not mean shared folders should never be used. You only need to move code reused by other routes into folders with a broader scope.
How Should layout, loading, and error Files Be Separated?
layout.tsx handles the shared UI shell. The root app/layout.tsx wraps all routes, while a layout.tsx in a child folder is applied in a nested manner to its subroutes. For example, if dashboard navigation is shared by /dashboard and /dashboard/reports, it can live in app/dashboard/layout.tsx. nextjs.org
page.tsx is the page UI shown at a specific path. If a layout is the repeating outer structure, a page is closer to the path-specific content that changes inside it. Separating them means you do not need to repeat shared navigation or frames in every page.
The App Router also provides reserved files for state-specific UI. loading.tsx is used for loading UI, error.tsx for error UI, and not-found.tsx for not-found UI. Unlike ordinary component files, Next.js interprets these files for specific roles, so they should be placed with their role and scope in mind. nextjs.org
For example, if you want to show a separate screen while data is being prepared under /dashboard, you can consider app/dashboard/loading.tsx; if you need a screen for error handling within that scope, you can consider app/dashboard/error.tsx. There is no need to add these files mechanically to every folder. Deciding based on whether each route actually needs separate waiting, error, or not-found state UI keeps the structure concise.
How Are Dynamic Routes and API Routes Represented in Folder Names?
Use bracket notation when part of a route is not predetermined. [slug] means one dynamic segment, [...slug] means all following subsegments, and [[...slug]] means an optional form where those subsegments may be absent. nextjs.org
For example, a page whose post identifier changes can be structured as follows.
src/app/posts/
└─ [slug]/
└─ page.tsx
In this structure, [slug] is not a fixed folder name; it is a placeholder that receives the changing part of the URL. By contrast, when multiple levels of a route must be handled by a single convention, consider [...slug] or [[...slug]]. Which notation to choose depends on whether at least one subpath must exist and whether the no-path case should also be handled by the same screen. nextjs.org
When building APIs, route.ts is the entry point for a Route Handler. Therefore, you can represent URL structure with folders such as app/api/users/route.ts and place route.ts at the end. For both pages and APIs, reading the folder hierarchy allows you to infer the approximate path. However, it is better to separate dedicated implementation files appropriately so that UI and server-side processing code do not become overly mixed and long in the same area. nextjs.org
Why Are Route Groups and Private Folders Needed?
A folder wrapped in parentheses, such as (marketing), is a route group. It is a logical grouping that is not included in the URL. For example, you can group marketing-oriented pages such as company information and pricing together, while managing product-use screens in a separate structure. app/(marketing)/about/page.tsx becomes the /about route without the group name. nextjs.org
Route groups are useful when you want to show layout boundaries or code ownership without changing the URL. However, because the group name does not appear in the URL, a conflict occurs if separate groups ultimately create the same URL. In addition, a configuration that moves between multiple root layouts can cause a full page load, so splitting root layouts should not be applied casually just to make folders look neat. nextjs.org
Folders that start with an underscore, such as _components and _lib, are private folders and are excluded from routing. In the App Router, ordinary files do not become routes by default, so an underscore is not strictly necessary. Still, it can be useful when you want to visibly mark the boundary between routing special files and internal implementation, or avoid confusion with reserved file names. nextjs.org
For example, app/dashboard/_components/summary-card.tsx communicates that it is dashboard-specific UI. However, if that component begins to be used repeatedly in other features, it is more natural to consider moving it out of the underscore folder into shared components or an appropriate feature boundary. Remember that a folder prefix is not an access-control mechanism; it is notation that communicates the role of the code.
How Should You Distinguish components, features, lib, hooks, and types?
These folders are optional organizational choices, not reserved App Router conventions. Therefore, the responsibility boundaries agreed on by the team matter more than the names themselves. The following distinctions are a common starting point.
| Folder | Code typically placed there | Placement criterion |
|---|---|---|
components | UI reused across multiple screens | Is it not tied to a specific URL or domain? |
features | Feature- or domain-level implementation | Is there a clear business concept, such as accounts, orders, or dashboards? |
lib | Shared utilities and clients | Is it a shared tool rather than UI? |
hooks | Reusable hooks | Do multiple components share the same state or behavior? |
types | Shared types | Do multiple areas reference the same type definitions? |
components can contain not only generic buttons but also composite UI shared across multiple features. However, making every UI element a globally shared component from the beginning can abstract implementations that are actually needed by only one screen. Another approach is to keep it near the route initially, then move it once it is stably used in two or more places and has a clear shared interface.
features is especially easy to read when a domain-centered structure is needed. For example, if orders and accounts each have independent screens, UI, and data-processing code, you can group them as features/orders and features/account. Conversely, creating too many feature folders in a simple site can force people to move across many folders just to find files. It is better to introduce them only when feature boundaries align with real product concepts.
lib is a candidate location for non-UI foundation code such as shared utilities or server clients. However, if every function file accumulates in lib, it can become a large, hard-to-understand storage area. A practical rule is to keep tools used only by one feature near that feature or route, and move only code shared in multiple places into lib.
Why Do public and Environment Variable Files Belong at the Project Root?
public is the project-root folder for static files. Files placed there are served from the root path; for example, public/profile.png is referenced as /profile.png. This provides one place for files that are served statically, such as images and fonts. nextjs.org
Even when using src, you should not think of the structure as moving public to src/public. public remains at the project root, and package.json, next.config.js, tsconfig.json, and .env.* are also managed from the root. In particular, because local environment variable files such as .env.local can contain secrets, it is important to establish an operational rule not to include them in version control. nextjs.org
Distinguishing the roles of static assets and application source makes paths easier to interpret. Files in src/app are code that builds screens or routing, while files in public are static assets referenced by URL. Even when the same image is used on a screen, you need to understand its delivery method and reference path differently depending on where the file is placed.
How Does This Differ from the Pages Router Structure?
The Pages Router works by treating files in the pages directory as routes. For example, pages/index.tsx corresponds to /, and pages/about.tsx corresponds to /about. Reserved file conventions also assign special roles to _app, _document, 404, 500, and others. nextjs.org
It is easy to make mistakes if you see the App Router and Pages Router as approaches that differ only in folder name. In the App Router, special files such as page.tsx, layout.tsx, and route.ts beneath folder segments divide responsibilities, while ordinary files are not routes by default. In the Pages Router, files inside pages are more directly connected to routes. nextjs.orgnextjs.org
When working with an existing Pages Router project, you should respect its current pages-based conventions. Conversely, when starting a new project, basing the structure on an App Router-centered example and adding organizational folders only when truly needed reduces overhead. It is important not to confuse the conventions of the two routers within the same directory design.
What Criteria Should You Use to Choose a Structure for a Real Project?
First, start with the URL structure. List the key paths users will access, then determine which shared layouts each path uses. Represent the result through folders in app and the placement of page.tsx and layout.tsx. Use route groups when there is a clear reason to divide screen areas or layouts without changing URLs. nextjs.orgnextjs.org
Second, assess the scope of reuse. Keep code used by only one route close to that route. UI used by multiple routes can move to a broader scope such as components, and utilities used by multiple features can move to lib. Rather than generalizing everything from the start, separating code when actual reuse and change patterns emerge helps reduce unnecessary abstraction.
Third, consider feature independence. If features with clear areas of responsibility and terminology—such as accounts, administration, or orders—grow larger, domain boundaries such as features can be useful. On the other hand, when there are few screens and distinctions between features are weak, colocation in app plus a small number of shared folders may be enough.
Fourth, check the team’s discovery cost. When a new team member looks for code for a particular URL, they should be able to follow the app path and find the page and dedicated implementation. The names and locations of shared buttons or utilities should also be predictable. A good structure comes from this predictability more than from fashionable folder names.
What Misconceptions and Structural Pitfalls Should You Avoid?
The first misconception is that “creating a folder immediately creates a URL.” In the App Router, a folder represents a segment, but it needs page.tsx or route.ts to become a public page or API endpoint. This rule allows related internal files to live together in the route folder. nextjs.org
The second misconception is that “without an underscore folder, all internal files are exposed.” Ordinary files in the App Router are not routes by default. A name such as _components is not a required security feature; it is an organizational tool that indicates internal implementation and excludes the folder from routing. nextjs.org
The third misconception is that “route group names are also included in URLs.” The parenthesized name in (marketing) is excluded from the URL. Because of this convenience, you should make sure different groups do not create the same final URL. If you split multiple root layouts, the possibility of a full page load when navigating between groups is another item to consider before designing the structure. nextjs.org
Finally, avoid the mistake of keeping matching app or pages directories in both the root and src while introducing src. Since the root takes precedence in this case, it can appear that the source you expected is not being executed. Changing a structure is not simply a task of adding folders all at once; proceed by verifying which directory actually serves as the basis for routing. nextjs.org
Conclusion: The Standard Is About Role Boundaries, Not a Folder List
The starting point for a Next.js project structure is the routing conventions defined by app and special files. In a new App Router project, you can make src/app the center for URLs, pages, and layouts; keep public as the static-asset folder at the root; and choose components, features, lib, hooks, and types according to the actual scope of code reuse and domain complexity. nextjs.orgnextjs.org
Ultimately, a good structure is not the one with the most folders. It is one where team members can easily predict the location of a screen for a particular route, code dedicated to that screen, and code shared across multiple places. Follow Next.js file conventions precisely, then adjust the organizational approach above them incrementally as the project’s growth rate and change patterns evolve.