
Understanding Angular 22 project structure is easy to overlook when starting out, but knowing where your own code should live is what matters once the project starts growing.
In this practical Angular 22 tutorial, we’ll build an Angular 22 project structure that scales beyond a demo, understand its important files and folders, create our first feature, and organize the project in a structure that can scale beyond a demo.
What We’ll Build
Instead of creating another basic Hello World application, we’ll start a small Task Manager project.
By the end, our application will have a structure similar to:
src/
├── app/
│ ├── core/
│ ├── features/
│ │ └── tasks/
│ ├── shared/
│ ├── app.config.ts
│ ├── app.routes.ts
│ ├── app.ts
│ └── app.html
├── index.html
├── main.ts
└── styles.css
This gives us a useful foundation for future Angular projects — and it’s the same Angular 22 project structure pattern you can reuse on any scaled app.
Prerequisites
Before starting, verify that Node.js and npm are installed.
node --version
npm --versionAngular’s current installation documentation should always be checked before installing because supported Node.js versions change over time.
Step 1: Install Angular CLI
Open your terminal:
npm install -g @angular/cliVerify the installation:
ng versionFor platform-specific setup details or troubleshooting, see the official Angular CLI setup guide. You should see information about Angular CLI, Angular, Node.js, npm and your operating system.
If you’re setting this up on Windows and run into any environment issues, check out our step-by-step guide to installing Angular on Windows for a more detailed walkthrough.
Step 2: Create an Angular 22 Application
Create the project:
ng new angular-task-managerAngular CLI will ask you several configuration questions.
For a modern application, routing is useful because our Task Manager may eventually contain pages such as:
/tasks
/tasks/new
/tasks/123
/settingsEnter the project:
cd angular-task-managerStart the development server:
ng serveThen visit:
http://localhost:4200A useful shortcut is:
ng serve --openAngular will compile the application and open it in your default browser.
Step 3: Understand main.ts
One of the first files Angular executes is:
src/main.tsA modern standalone Angular application bootstraps the root component rather than starting with the old NgModule-first architecture.
Conceptually, you’ll see something similar to:
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { App } from './app/app';
bootstrapApplication(App, appConfig)
.catch(err => console.error(err));Think of main.ts as the starting point of the Angular application.
It tells Angular:
Start this application using this root component and configuration.
Most applications rarely require frequent changes to this file.
Step 4: Understand app.config.ts
Open:
src/app/app.config.tsApplication-level providers can be configured here.
For example:
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};Later you may configure things such as HTTP, routing, animations or other application-wide services here.
A useful mental model is:
main.ts
↓
app.config.ts
↓
root component
↓
featuresStep 5: Understand app.routes.ts
Routing configuration belongs in:
src/app/app.routes.tsFor example:
import { Routes } from '@angular/router';
export const routes: Routes = [];We’ll add feature routes later.
Keeping routing separate prevents the root component from becoming responsible for navigation configuration.
Step 6: Don’t Put Everything Inside app/
A common beginner structure looks like:
app/
├── task-list
├── task-form
├── navbar
├── auth-service
├── task-service
├── user-service
├── button
├── modal
└── ...This works initially.
After several months, however, developers struggle to answer:
- Which files belong together?
- Which service is global?
- Which component is reusable?
- Where should the next feature go?
A better structure organizes code according to responsibility.
Step 7: Create core, shared and features (the core of your Angular 22 project structure)
Inside src/app, create:
core/
shared/
features/Their responsibilities should be different.
core/
Use core for application-wide functionality.
Example:
core/
├── guards/
├── interceptors/
├── services/
└── models/Potential examples:
auth.service.ts
auth.guard.ts
api.interceptor.tsAvoid putting ordinary feature components here.
shared/
Use shared for reusable UI and utilities.
Example:
shared/
├── components/
├── directives/
├── pipes/
└── utils/Examples:
button/
modal/
loading-spinner/
truncate.pipe.tsThe important test is:
Could multiple unrelated features reasonably reuse this?
If yes, shared may be appropriate.
features/
Feature-specific functionality belongs here.
For our application:
features/
└── tasks/Later we might add:
features/
├── tasks/
├── authentication/
├── dashboard/
└── settings/This is easier to navigate because code that changes together generally lives together.
Step 8: Generate the Task List
Run:
ng generate component features/tasks/task-listor:
ng g c features/tasks/task-listAngular CLI creates the component files for us.
For a deeper look at how Angular components are structured and how they fit together, see our Angular Components tutorial.
Now add a simple implementation:
import { Component } from '@angular/core';
@Component({
selector: 'app-task-list',
standalone: true,
template: `
<section>
<h2>My Tasks</h2>
<ul>
<li>Learn Angular project structure</li>
<li>Build task component</li>
<li>Add routing</li>
</ul>
</section>
`
})
export class TaskList {}We now have actual feature code instead of a meaningless demo component.
Step 9: Add a Task Route
Update app.routes.ts:
import { Routes } from '@angular/router';
import { TaskList } from './features/tasks/task-list/task-list';
export const routes: Routes = [
{
path: 'tasks',
component: TaskList
},
{
path: '',
redirectTo: 'tasks',
pathMatch: 'full'
}
];The application now redirects / to /tasks.
Step 10: Add router-outlet
The root component needs somewhere to render the active route.
Import RouterOutlet and use:
<router-outlet></router-outlet>Now visiting:
http://localhost:4200/tasksrenders our Task List.
Step 11: Create a Task Interface
Inside the tasks feature, create:
task.model.tsAdd:
export interface Task {
id: number;
title: string;
completed: boolean;
}Then our feature can work with properly typed data:
tasks: Task[] = [
{
id: 1,
title: 'Learn Angular Signals',
completed: false
},
{
id: 2,
title: 'Build Task Manager',
completed: true
}
];This is much closer to real application development.
Step 12: Angular 22 Project Structure as the Application Grows
Our project can eventually become:
src/app/
├── core/
│ ├── guards/
│ ├── interceptors/
│ └── services/
│
├── shared/
│ ├── components/
│ ├── directives/
│ └── pipes/
│
├── features/
│ ├── authentication/
│ ├── dashboard/
│ ├── tasks/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── services/
│ │ ├── task.model.ts
│ │ └── task.routes.ts
│ └── settings/
│
├── app.config.ts
├── app.routes.ts
├── app.ts
└── app.htmlDon’t create every folder on day one simply because an architecture diagram contains it.
Create structure when the project actually needs it.
Common Mistake #1: One Giant components Folder
Avoid:
components/
├── task-list
├── login
├── dashboard
├── settings
├── profile
└── navbarThese components do not necessarily belong to the same feature.
Prefer:
features/tasks/
features/authentication/
features/dashboard/Common Mistake #2: Making Everything Shared
A component isn’t shared merely because it could theoretically be reused.
If TaskCard exists only for the Tasks feature, keep it inside:
features/tasks/Move something to shared when genuine cross-feature reuse exists.
Common Mistake #3: Creating Services Without Clear Ownership
Ask:
Is this service application-wide or feature-specific?
For example:
core/services/auth.service.tsmakes sense for global authentication.
But:
features/tasks/services/task.service.tsis usually a better home for task-specific API logic.
Practical Architecture Rule
Use this decision process:
Is it a business feature?
↓
features/
Used by multiple features?
↓
shared/
Application-wide infrastructure?
↓
core/Following this rule consistently is what keeps your Angular 22 project structure predictable as the app grows, instead of turning into a folder free-for-all.
Final Angular 22 Project Structure
For our current application:
src/
└── app/
├── core/
├── features/
│ └── tasks/
│ ├── task-list/
│ └── task.model.ts
├── shared/
├── app.config.ts
├── app.routes.ts
├── app.ts
└── app.htmlKey Takeaways
Angular 22 project structure isn’t about creating as many folders as possible.
The goal is to make the codebase predictable.
Remember:
main.tsstarts the application.app.config.tscontains application-level configuration.app.routes.tsdefines top-level routes.features/contains business functionality.shared/contains genuinely reusable pieces.core/contains application-wide infrastructure.- Prefer organizing by feature as the application grows.
A clean structure makes future work such as lazy loading, Signals, authentication, API integration and testing significantly easier.
In the next tutorial, we’ll use Angular Signals to turn this Task Manager into a reactive application.
