Angular v20+ Folder Structure Guide: Best Practices for Scalable Apps
Learn to create a scalable and maintainable Angular v20+ folder structure. Follow the latest Angular 20 style guide and best practices for organizing your app.
This article follows the official Angular 20 style guide:
Component, directive, and service files/classes no longer use a suffix.
Pipes, guards, resolvers, interceptors, and modules keep their suffix , but use a
-
(e.g.,auth-guard.ts
).
All folder and file structure examples in this guide reflect these new conventions.
But it does not totally comply with the folder structure as
Angular discourages having components
, and services
folders without restrictions. If that’s a practice not used at the root of the
project, it’s used in nested folders when organizing features.
For example, a feature might have a components
folder, and a
services
folder to make the code more discoverable and easier to
maintain.
Starting with Angular 17 release, creating an Angular application with ng new
will generate a Standalone application by default, from Standalone Components and Directives to bootstrapping the application with a Standalone AppComponent and getting rid of NgModules. Not only it introduces some changes in the way you organized your application but that’s a good opportunity to review the architecture itself by now!
This article also adopts the new official Angular style guide released with Angular 20 version, applying the following changes:
-
Components, Directives and Services do not use a suffix anymore, both for the class and the file name.
-
Pipes, Guards, Resolvers, Interceptors and Modules keep the suffix on the class name but the suffix is now separated with a
-
instead of a.
, likeauth-guard.ts
instead ofauth.guard.ts
.
This article focuses on the content of your src/app
folder, and how to organize it to make it easier to maintain and scale.
For convenience, the structure examples won’t include the following default files created at the root of the src/app
folder by the Angular CLI:
src└── app ├── app.ts ├── app.spec.ts ├── app.html ├── app.css ├── app.routes.ts ├── app.config.ts
Types of content
There are three types of code content in the src/app
folder:
- the code specific to a non-business feature
- the code specific to a business feature
- code shared between features
That’s commonly translated into the following folder structure:
src└── app ├── core ├── features └── shared
The
features
folder might not be that simple and will be covered in the next section. That’s the first level of organization of your application, with a dedicated folder for each type of content.
Non-business feature (core folder)
A non-business feature is a feature that is not specific to the business domain of the application.
Some examples are:
- the layout of your application
- the authentication process
- the global notifications
For this content, you’ll want to create a dedicated folder in the src/app/core
folder.
src└── app └── core ├── layout └── auth
Let’s take a closer look at the auth
folder as an example!
Authentication
Common features of an authentication process are:
- the login page
- the registration page
- the password recovery page
You will also want to define:
- the user model
- the authentication service to communicate with the server
- the authentication guard to protect routes
Let’s organize these files!
Pages
We want to differentiate the routed components from the rest of the code. To do so, create a dedicated folder for the pages of the feature and a routing file to define the routes of the feature.
At this point your core folder will look like this:
src└── app └── core └── auth ├── auth.routes.ts └── pages ├── login ├── register ├── password-recovery
If your feature only includes 1 route, you can declare it in your
src/app/app.routes.ts
file. In this situation, do not create a dedicatedauth.routes.ts
file.
Global authentication code
While being specific to the authentication feature, some code is shared between the different pages of the feature, or even more.
The user model and the authentication service are shared between the different pages of the auth feature.
The authentication guard is shared between the different pages of the whole application.
src└── app └── core └── auth ├── auth-store.ts ├── auth-store.spec.ts ├── auth.model.ts ├── auth-guard.ts ├── auth-guard.spec.ts ├── auth.routes.ts └── pages ├── login ├── register ├── password-recovery
If you know that you’ll have more than one related file in the future, create dedicated folders for each type of content from the beginning:
src└── app └── core └── auth ├── models └── auth.model.ts ├── guards └── auth-guard.ts └── auth-guard.spec.ts ├── services ├── auth-store.ts └── auth-store.spec.ts ├── auth.routes.ts └── pages ├── login ├── register ├── password-recovery
Pages folders
The pages folders are used to store the routed component and all the related and specific code of a page. At the root of the folder, you’ll find the routed component, and more folders for content shared between the different pages of the feature:
components
for the components used in the pagesservices
for the services used in the pagesmodels
for the models used in the pagesdirectives
for the directives used in the pagespipes
for the pipes used in the pages
The folder will look like this:
src└── app └── core └── auth └── pages ├── login ├── login.ts ├── login.spec.ts ├── login.html ├── login.css ├── components ├── services ├── models ├── directives ├── pipes ├── register ├── password-recovery
Apply the same logic to the other features of your core folder.
Isolated non-business feature
Some features might be as simple as a service or a directive.
To simplify your structure, you might not want to create a dedicated folder for them in the src/app/core
folder.
In this situation, place the code directly at the root of the folder, in a folder based on its technical shape (services, directives, pipes, …).
Such
services
,directives
, andpipes
folders are not always encouraged, as wrapping files in a technical way can reduce their discoverability. However it makes sense for limited scopes
Like these notification-api.ts
and api-interceptor.ts
files:
src└── app └── core ├── services └── notification-api.ts └── notification-api.spec.ts ├── interceptors └── api-interceptor.ts └── api-interceptor.spec.ts ├── auth ├── auth-store.ts ├── auth-store.spec.ts ├── auth.model.ts ├── auth-guard.ts ├── auth-guard.spec.ts ├── auth.routes.ts └── pages ├── login ├── register ├── password-recovery
Business feature
A business feature is a feature that is specific to the business domain of the application. Some examples are for e-commerce applications: the product list, the loyalty program, the cart, and the checkout process.
The common way to organize these features is to nest them in the src/app/features
folder.
However such a practice does not scale well!
An application often starts around a single domain, like posting messages for a LinkedIn application. Then your application grows and you start to have more and more features, like the groups, the events, the jobs, and so on.
Putting each of these features on the same level will quickly lead to a messy structure you’ll have to maintain.
A better approach is to group features by domain!
Rather than including all features in a single folder, such as src/app/features
:
src└── app └── features ├── post-creation ├── post-list ├── mention-list ├── jobs-card ├── group-card ├── group-form ├── event-card ├── event-form ├── job-card ├── job-form
You can group them by domain, in nested folders:
src└── app └── modules ├── posts ├── post-creation ├── mention-list └── post-list ├── groups ├── group-card └── group-form ├── events ├── event-card └── event-form ├── jobs ├── job-card └── job-form
The
modules
folder is a common name for this kind of organization, it includes features triaged by domain.
Besides this meta organization, features use the same logic as the core folder:
- a dedicated folder for each feature
- a dedicated folder for the pages of the feature
- more folders for content shared between the different pages of the feature
There is one exception though!
While you can have some isolated pieces of code at the root of the src/app/core
folder, you won’t have any at the root of the src/app/modules
folder.
It’s all about business isolated features.
src└── app └── modules ├── posts ├── post-creation ├── mention-list └── post-list ├── groups ├── group-card └── group-form ├── events ├── event-card └── event-form ├── jobs ├── job-card └── job-form
If you need to share some code between multiple features of a same domain, put it at the root of the domain folder.
src└── app └── modules ├── posts ├── post-creation ├── mention-list └── post-list post.model.ts
Shared code
The shared code is the code that is shared between the different features of the application. There are two kinds of code being shared between features:
- the one including some business logic
- the one that doesn’t.
The shared code lives in the src/app/shared
folder.
Dumbed shared code
You might have heard about dumb components and smart components. The dumb components are the components that don’t include any business logic.
Let’s take a notification component as an example.
Even if the data to fill it comes from a business feature, the notification should not care.
If it expects a title and a message, it should not care about the origin of the title and the message.
And its Signal input()
should be named title
and message
, not productTitle
and productMessage
.
Such a component is perfectly suited to be placed in the src/app/shared
folder.
Such components, pipes and some util functions are the perfect example of ‘dumb’ shared code.
src└── app └── shared ├── components ├── notification.ts ├── notification.spec.ts ├── notification.html ├── notification.css ├── pipes ├── date-pipe.ts ├── date-pipe.spec.ts ├── utils ├── array.utils.ts ├── array.utils.spec.ts
Smart shared code
The ‘smart’ shared code is the code that includes some business logic. Where to put it as it’s both shared and business specific?
There are two strategies to handle this situation:
- place this content in the
src/app/shared
folder - place this content in the related feature folder in the
src/app/modules
folder
I encourage to use the second solution, as it makes the code more discoverable and easier to maintain. That’s quite common for a feature to include some part of another one.
It can be about Shared UI content, like a product-card
component, or shared business logic, like a product-api
or a product.model
.
Placing it in the shared folder will quickly lead to have too much content there, making it hard to maintain.
Here is a summary of the whole application structure:
src└── app ├── core ├── layout ├── auth ├── auth-store.ts ├── auth-store.spec.ts ├── auth.model.ts ├── auth-guard.ts ├── auth-guard.spec.ts ├── auth.routes.ts └── pages ├── login ├── register ├── password-recovery ├── services └── notification-api.ts └── notification-api.spec.ts ├── interceptors └── api-interceptor.ts └── api-interceptor.spec.ts └── features ├── product ├── cart ├── checkout ├── checkout-api.ts ├── checkout-api.spec.ts ├── checkout.model.ts ├── checkout-guard.ts ├── checkout-guard.spec.ts ├── checkout.routes.ts └── pages ├── address ├── payment └── shared ├── components ├── notification.ts ├── notification.spec.ts ├── notification.html ├── notification.css ├── pipes ├── date-pipe.ts ├── date-pipe.spec.ts ├── utils ├── array.utils.ts ├── array.utils.spec.ts
Angular RealWorld Example App is a good example of a real-world application using this structure.