Angular 4 🅰️ Project Structure Explained
Every Angular project created by ng new has the same layout. The files fall into three groups — configuration at the root, source code in src/, and dependencies in node_modules/. Knowing what each file does tells you where to make changes and why the CLI put things where it did.
Key point: Angular’s structure is a convention, not a requirement. The CLI generates this layout because it works well for most projects and matches what the framework expects by default. You can change it, but the default is well-designed — learn it before you modify it.
The top-level layout
After ng new my-app, the project looks like this:
my-app/
├── angular.json
├── package.json
├── package-lock.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.spec.json
├── .editorconfig
├── .gitignore
├── README.md
├── src/
│ ├── main.ts
│ ├── index.html
│ ├── styles.css
│ └── app/
│ ├── app.component.ts
│ ├── app.component.html
│ ├── app.component.css
│ ├── app.component.spec.ts
│ └── app.config.ts
├── public/
│ └── favicon.ico
└── node_modules/
Each file has a specific role. The rest of this chapter walks through them.
Why this shape: Configuration lives at the root, source lives in
src/, and generated assets live inpublic/. Dependencies are isolated innode_modules/and never edited by hand. That separation keeps the project clean.
Configuration files
These live at the root and control how the project builds, runs, and tests.
angular.json — the CLI’s configuration. It defines:
- Projects in the workspace
- Build, serve, and test targets
- Output paths
- Asset globs
- Global styles and scripts
- Environment-specific configurations
Almost every CLI command reads this file. ng build, ng serve, and ng test all use the options defined here.
package.json — the npm manifest. It lists:
- The project name and version
- Scripts (
ng serve,ng build,ng test) - Dependencies (
@angular/core,rxjs,zone.js) - Dev dependencies (
@angular/cli,typescript, test tooling)
Running npm install reads this file and installs everything into node_modules/.
package-lock.json — the exact versions of every dependency, including transitive ones. Commit this file so everyone installs the same thing.
tsconfig.json — the base TypeScript configuration. Defines compiler options that other tsconfigs inherit.
tsconfig.app.json — the TypeScript config for the application build. Extends the base and points to main.ts.
tsconfig.spec.json — the TypeScript config for tests. Extends the base and includes spec files.
.editorconfig — editor settings like indentation and line endings. Keeps different editors consistent.
.gitignore — tells Git what not to track — usually node_modules/, dist/, and editor-specific files.
README.md — project documentation. The CLI generates a basic one.
Why so many configs: Each target (app, tests, base) needs slightly different TypeScript settings. The base holds shared options; each target extends it and adds what it needs. This keeps things DRY and consistent.
The src/ folder
This is where your actual application lives.
main.ts — the entry point. It bootstraps the application, loading the root component or the app configuration.
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error(err));
Modern Angular uses bootstrapApplication with a root component and configuration object.
index.html — the host page. Angular renders the app inside a tag on this page — usually <app-root></app-root>.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>
The <base href="/"> tag tells Angular how to resolve relative URLs — important for routing.
styles.css — global styles. Applied to the whole app. Component styles are separate.
src/app/ — the application code. Contains the root component and, as you build, everything else.
Why
index.htmlisn’t the app: Angular’s components render inside<app-root>, but the HTML around it is minimal. The framework owns everything inside the root tag.
The src/app/ folder
The root application module, component, and configuration live here.
app.component.ts — the root component. The top of the component tree.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
}
The @Component decorator wires the class to a template and styles.
app.component.html — the root template. Contains the app’s initial markup — often just a router outlet or a shell.
<h1>{{ title }}</h1>
<router-outlet></router-outlet>
app.component.css — component-scoped styles.
app.component.spec.ts — the test file. Scaffolded by default; delete it if you don’t want tests.
app.config.ts — application-level configuration. Provides the router, HTTP client, and other services to the app.
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
Modern Angular uses this file to configure the app’s global providers.
app.routes.ts — the route definitions. Where you declare which URL renders which component.
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
Why
app.config.tsexists: Older Angular usedAppModulefor global configuration. Standalone components replaced that with theApplicationConfigobject — cleaner and more explicit.
The public/ folder
Static assets that the build copies as-is. Favicon, robots.txt, images that don’t need processing.
public/
├── favicon.ico
└── ...
Anything in public/ is served at the root of the deployed app. Referenced with absolute paths like /favicon.ico.
Why
public/instead ofassets/: Older Angular versions usedsrc/assets/. Modern Angular usespublic/— same idea, but at the root and simpler.
The node_modules/ folder
Where npm installs dependencies. This folder is:
- Huge — often 200MB or more
- Reproducible — regenerated by
npm installfrompackage.jsonand the lockfile - Never edited by hand
- Always in
.gitignore
You don’t look at node_modules/ unless you’re debugging a dependency. The CLI knows where everything is.
Why not commit it: Dependencies are reproducible from
package.jsonandpackage-lock.json. Committing them would bloat the repository without benefit.
The dist/ folder
Created by ng build. Contains the compiled app — HTML, JavaScript bundles, CSS, and assets, all ready to deploy.
dist/my-app/
├── browser/
│ ├── index.html
│ ├── main.a1b2c3d4.js
│ ├── polyfills.e5f6g7h8.js
│ └── styles.i9j0k1l2.css
└── server/ (if SSR)
You deploy the contents of dist/, not the source. The folder is also in .gitignore.
Folder organization as the app grows
ng new gives you a minimal layout — one component, one config. Real apps grow into something more structured.
Common conventions:
features/— one folder per feature, holding its components, services, and routesshared/— components, directives, and pipes used across featurescore/— singleton services, guards, interceptorsmodels/— TypeScript interfaces and typeslayout/— header, footer, sidebar components
An example:
src/app/
├── core/
│ ├── auth.service.ts
│ └── auth.guard.ts
├── shared/
│ ├── components/
│ └── pipes/
├── features/
│ ├── users/
│ │ ├── user-list/
│ │ ├── user-detail/
│ │ └── users.routes.ts
│ └── orders/
├── layout/
│ ├── header/
│ └── footer/
├── app.component.ts
├── app.config.ts
└── app.routes.ts
Each feature owns its components and routes. Shared code lives in shared/. Singletons live in core/.
Why organize by feature: Feature folders group related code together, so everything about “users” is in one place. That’s easier to navigate than organizing by file type (
components/,services/,pipes/) as the app grows.
Environment files
Older Angular versions had src/environments/environment.ts and environment.prod.ts. The CLI generated them for environment-specific settings.
Modern Angular still supports this pattern, but with the fileReplacements option in angular.json, you can also use it more flexibly.
// environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:3000'
};
// environments/environment.prod.ts
export const environment = {
production: true,
apiUrl: 'https://api.example.com'
};
Import and use environment.apiUrl anywhere in the app.
Why separate files: Development and production use different backends. Environment files keep those settings in one place per environment — no
if (production)scattered throughout the code.
Complete Example Session
# ============================================
# PART 1: CREATE PROJECT
# ============================================
ng new my-app
cd my-app
# ============================================
# PART 2: LIST FILES
# ============================================
ls -la
# [ .editorconfig ]
# [ .gitignore ]
# [ angular.json ]
# [ package.json ]
# [ tsconfig.json ]
# [ src/ ]
# [ public/ ]
# [ node_modules/ ]
# ============================================
# PART 3: EXPLORE src/
# ============================================
ls src/
# [ app/ index.html main.ts styles.css ]
ls src/app/
# [ app.component.css ]
# [ app.component.html ]
# [ app.component.spec.ts ]
# [ app.component.ts ]
# [ app.config.ts ]
# [ app.routes.ts ]
# ============================================
# PART 4: VIEW main.ts
# ============================================
cat src/main.ts
# [ import { bootstrapApplication } from '@angular/platform-browser'; ]
# [ import { AppComponent } from './app/app.component'; ]
# [ import { appConfig } from './app/app.config'; ]
# [ ]
# [ bootstrapApplication(AppComponent, appConfig) ]
# [ .catch(err => console.error(err)); ]
# ============================================
# PART 5: VIEW index.html
# ============================================
cat src/index.html
# [ <!doctype html> ]
# [ <html lang="en"> ]
# [ <head> ]
# [ <meta charset="utf-8"> ]
# [ <title>MyApp</title> ]
# [ <base href="/"> ]
# [ <meta name="viewport" content="width=device-width, initial-scale=1"> ]
# [ </head> ]
# [ <body> ]
# [ <app-root></app-root> ]
# [ </body> ]
# [ </html> ]
# ============================================
# PART 6: VIEW app.component.ts
# ============================================
cat src/app/app.component.ts
# [ import { Component } from '@angular/core'; ]
# [ import { RouterOutlet } from '@angular/router'; ]
# [ ]
# [ @Component({ ]
# [ selector: 'app-root', ]
# [ imports: [RouterOutlet], ]
# [ templateUrl: './app.component.html', ]
# [ styleUrl: './app.component.css' ]
# [ }) ]
# [ export class AppComponent { ]
# [ title = 'my-app'; ]
# [ } ]
# ============================================
# PART 7: VIEW app.config.ts
# ============================================
cat src/app/app.config.ts
# [ import { ApplicationConfig } from '@angular/core'; ]
# [ import { provideRouter } from '@angular/router'; ]
# [ import { routes } from './app.routes'; ]
# [ ]
# [ export const appConfig: ApplicationConfig = { ]
# [ providers: [provideRouter(routes)] ]
# [ }; ]
# ============================================
# PART 8: ADD A FEATURE FOLDER
# ============================================
mkdir -p src/app/features/users
ng g c features/users/user-list
# [ CREATE src/app/features/users/user-list/user-list.component.ts ]
# [ ... ]
# ============================================
# PART 9: VIEW angular.json
# ============================================
cat angular.json | head -30
# [ { ]
# [ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", ]
# [ "version": 1, ]
# [ "newProjectRoot": "projects", ]
# [ "projects": { ]
# [ "my-app": { ]
# [ "projectType": "application", ]
# [ "root": "", ]
# [ "sourceRoot": "src", ]
# [ ... ]
Quick Reference
Root Files
| File | Purpose |
|---|---|
angular.json | CLI build/serve/test config |
package.json | Dependencies, scripts |
package-lock.json | Exact versions |
tsconfig.json | Base TypeScript config |
tsconfig.app.json | App build config |
tsconfig.spec.json | Test config |
.editorconfig | Editor settings |
.gitignore | Files Git ignores |
README.md | Documentation |
src/ Files
| File | Purpose |
|---|---|
main.ts | Entry point — bootstraps the app |
index.html | Host page |
styles.css | Global styles |
app/ | Application code |
src/app/ Files
| File | Purpose |
|---|---|
app.component.ts | Root component class |
app.component.html | Root template |
app.component.css | Root styles |
app.component.spec.ts | Root tests |
app.config.ts | App-level providers |
app.routes.ts | Route definitions |
Generated Folders
| Folder | Purpose | Tracked by Git? |
|---|---|---|
node_modules/ | Dependencies | ❌ |
dist/ | Build output | ❌ |
public/ | Static assets | ✅ |
src/ | Source code | ✅ |
Common Folder Conventions
| Folder | Purpose |
|---|---|
features/ | Per-feature code |
shared/ | Reusable components, pipes |
core/ | Singleton services, guards |
models/ | Types and interfaces |
layout/ | Header, footer, sidebar |
Environment Files
| File | Purpose |
|---|---|
environment.ts | Development settings |
environment.prod.ts | Production settings |
Configured via fileReplacements in angular.json.
Best Practices
✅ Do This:
# Keep the default structure until it stops working
# Don't reorganize on day one # ✅
# Organize by feature as the app grows
src/app/features/users/ # ✅
# Put shared code in shared/
src/app/shared/components/ # ✅
# Put singleton services in core/
src/app/core/auth.service.ts # ✅
# Never edit node_modules
# Install with npm # ✅
# Commit package-lock.json
git add package-lock.json # ✅
# Keep index.html minimal
<app-root></app-root> # ✅
❌ Don’t Do This:
# Don't edit node_modules
nano node_modules/... # ❌
# Don't commit node_modules or dist
git add node_modules # ❌
# Don't put business logic in main.ts
# Keep it minimal # ⚠️
# Don't use app.component.ts for feature code
# It's the shell # ⚠️
# Don't create random folders at root
# Everything goes in src/ # ❌
# Don't rename src/app
# The CLI expects it # ⚠️
# Don't edit angular.json without knowing why
# It affects every build # ⚠️
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Editing node_modules/ | Overwritten on install | Install via npm |
Committing node_modules/ | Huge repository | Add to .gitignore |
Confusing main.ts and app.component.ts | Wrong place for code | Bootstrap vs shell |
Forgetting package-lock.json | Version drift | Commit it |
Too much in app.component | Becomes a god component | Split into features |
Deleting app.routes.ts | Routing stops working | Keep it |
| Random root folders | Confusing structure | Use src/ |
Editing angular.json blindly | Breaks builds | Understand before changing |
Real-World Examples
1. Inspect the root
ls -la
Shows config files and source folders.
2. Read main.ts
cat src/main.ts
Bootstraps the app.
3. Read app.component.ts
cat src/app/app.component.ts
The root component.
4. Read app.config.ts
cat src/app/app.config.ts
Global providers.
5. Add a feature folder
mkdir -p src/app/features/users
ng g c features/users/user-list
Creates a component inside the feature.
6. View angular.json
cat angular.json
Build configuration.
7. Add an environment file
mkdir -p src/environments
echo "export const environment = { production: false, apiUrl: '/api' };" > src/environments/environment.ts
Simple environment setting.
8. Configure fileReplacements
In angular.json, under configurations.production:
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
9. Add assets
cp logo.png public/logo.png
Available at /logo.png after build.
10. Verify what’s ignored
cat .gitignore
# [ /node_modules ]
# [ /dist ]
# [ .angular ]
Visual: The Project Tree
my-app/
├── angular.json ← CLI config
├── package.json ← npm manifest
├── tsconfig.json ← TS base
│
├── src/ ← your code
│ ├── main.ts ← bootstrap
│ ├── index.html ← host page
│ ├── styles.css ← global styles
│ └── app/ ← root component + config
│
├── public/ ← static assets
│
├── dist/ ← build output (ignored)
└── node_modules/ ← dependencies (ignored)
Visual: The Bootstrap Chain
┌──────────────────────────────────────────────┐
│ index.html │
│ │ │
│ ▼ │
│ <app-root></app-root> │
│ │ │
│ ▼ │
│ main.ts │
│ │ │
│ ▼ │
│ bootstrapApplication(AppComponent, config) │
│ │ │
│ ▼ │
│ AppComponent renders inside <app-root> │
│ │
└──────────────────────────────────────────────┘
Visual: Common Feature Layout
src/app/
├── core/ ← singletons
│ ├── auth.service.ts
│ └── auth.guard.ts
│
├── shared/ ← reusable
│ ├── components/
│ └── pipes/
│
├── features/ ← per-feature
│ ├── users/
│ │ ├── user-list/
│ │ ├── user-detail/
│ │ └── users.routes.ts
│ └── orders/
│
├── layout/ ← shell parts
│ ├── header/
│ └── footer/
│
├── app.component.ts
├── app.config.ts
└── app.routes.ts
Visual: What’s Tracked vs Ignored
┌──────────────────────────────────────────────┐
│ Tracked by Git │
│ │
│ src/ │
│ public/ │
│ angular.json │
│ package.json │
│ package-lock.json │
│ tsconfig*.json │
│ .gitignore │
│ README.md │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Ignored by Git │
│ │
│ node_modules/ ← regenerated by npm │
│ dist/ ← regenerated by build │
│ .angular/ ← CLI cache │
│ *.log │
│ .env │
│ │
└──────────────────────────────────────────────┘
Summary
| Location | Contains |
|---|---|
| Root | Config files (angular.json, package.json, tsconfigs) |
src/ | Application source code |
src/app/ | Root component, config, routes |
public/ | Static assets |
node_modules/ | Dependencies (ignored) |
dist/ | Build output (ignored) |
features/ | Per-feature code (as you grow) |
core/ | Singleton services and guards |
shared/ | Reusable components and pipes |
environments/ | Environment-specific settings |
Key takeaways:
- Angular projects follow a consistent structure created by
ng new - Configuration lives at the root —
angular.json,package.json, tsconfigs - Source code lives in
src/ main.tsbootstraps the app;app.component.tsis the root componentapp.config.tsholds global providers;app.routes.tsdefines routesindex.htmlis a minimal host page with<app-root>public/holds static assets served at the rootnode_modules/anddist/are generated and ignored by Git- Organize as you grow —
features/,shared/,core/ - Use environment files for per-environment settings
- Never edit
node_modules/— always install via npm
Remember: The structure is designed for clarity — configuration at the root, source in src/, generated folders ignored. As the app grows, keep features together and shared code separate. Learn the default layout first; only reorganize when the defaults stop working for you.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!