| |

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 in public/. Dependencies are isolated in node_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.html isn’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.ts exists: Older Angular used AppModule for global configuration. Standalone components replaced that with the ApplicationConfig object — 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 of assets/: Older Angular versions used src/assets/. Modern Angular uses public/ — 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 install from package.json and 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.json and package-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 routes
  • shared/ — components, directives, and pipes used across features
  • core/ — singleton services, guards, interceptors
  • models/ — TypeScript interfaces and types
  • layout/ — 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

FilePurpose
angular.jsonCLI build/serve/test config
package.jsonDependencies, scripts
package-lock.jsonExact versions
tsconfig.jsonBase TypeScript config
tsconfig.app.jsonApp build config
tsconfig.spec.jsonTest config
.editorconfigEditor settings
.gitignoreFiles Git ignores
README.mdDocumentation

src/ Files

FilePurpose
main.tsEntry point — bootstraps the app
index.htmlHost page
styles.cssGlobal styles
app/Application code

src/app/ Files

FilePurpose
app.component.tsRoot component class
app.component.htmlRoot template
app.component.cssRoot styles
app.component.spec.tsRoot tests
app.config.tsApp-level providers
app.routes.tsRoute definitions

Generated Folders

FolderPurposeTracked by Git?
node_modules/Dependencies
dist/Build output
public/Static assets
src/Source code

Common Folder Conventions

FolderPurpose
features/Per-feature code
shared/Reusable components, pipes
core/Singleton services, guards
models/Types and interfaces
layout/Header, footer, sidebar

Environment Files

FilePurpose
environment.tsDevelopment settings
environment.prod.tsProduction 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

PitfallProblemSolution
Editing node_modules/Overwritten on installInstall via npm
Committing node_modules/Huge repositoryAdd to .gitignore
Confusing main.ts and app.component.tsWrong place for codeBootstrap vs shell
Forgetting package-lock.jsonVersion driftCommit it
Too much in app.componentBecomes a god componentSplit into features
Deleting app.routes.tsRouting stops workingKeep it
Random root foldersConfusing structureUse src/
Editing angular.json blindlyBreaks buildsUnderstand 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

LocationContains
RootConfig 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.ts bootstraps the app; app.component.ts is the root component
  • app.config.ts holds global providers; app.routes.ts defines routes
  • index.html is a minimal host page with <app-root>
  • public/ holds static assets served at the root
  • node_modules/ and dist/ 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!