Project Structure
When you run ng new, the Angular CLI scaffolds a complete, build-ready project with a deliberate layout. Modern Angular (17/18/19) generates standalone projects by default — there is no root AppModule, and bootstrapping happens through an application config object instead. Understanding what each file does saves you from cargo-culting changes and makes it obvious where new code belongs. This page walks through the top-level files, the src folder, and the key configuration entry points that wire everything together.
The generated layout
A freshly scaffolded project (ng new my-app) produces a structure like this. The exact contents vary with the flags you pass — for example, adding routing or choosing a stylesheet format — but the shape is consistent.
my-app/
├── src/
│ ├── app/
│ │ ├── app.component.ts
│ │ ├── app.component.html
│ │ ├── app.component.css
│ │ ├── app.component.spec.ts
│ │ ├── app.config.ts
│ │ └── app.routes.ts
│ ├── index.html
│ ├── main.ts
│ └── styles.css
├── public/
│ └── favicon.ico
├── angular.json
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.spec.json
└── .editorconfig
Tip: In Angular 19, the CLI omits standalone-related boilerplate entirely — components are standalone by default and the
standalone: trueflag is no longer written into the decorator. If you seestandalone: trueeverywhere, you’re looking at older-style output.
Top-level files
The root of the project holds configuration that applies to the whole workspace and the npm package manifest.
| File | Purpose |
|---|---|
angular.json | Workspace/build configuration consumed by the CLI: build targets, file replacements, budgets, and asset paths. |
package.json | npm dependencies and the scripts (start, build, test) you invoke with npm run. |
tsconfig.json | Base TypeScript compiler options shared across the workspace. |
tsconfig.app.json | Application-specific TS config that extends the base and lists app source files. |
tsconfig.spec.json | TS config used when compiling unit-test files. |
public/ | Static assets copied verbatim into the build output (favicon, images, robots.txt). |
angular.json
angular.json is the CLI’s source of truth. It defines one or more projects, each with named targets (build, serve, test) and configurations (production, development). When you run ng build, the CLI reads the matching target to know which builder to use, where the entry point is, and what output options apply.
{
"projects": {
"my-app": {
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/my-app",
"browser": "src/main.ts",
"index": "src/index.html",
"tsConfig": "tsconfig.app.json",
"assets": [{ "glob": "**/*", "input": "public" }],
"styles": ["src/styles.css"]
}
}
}
}
}
}
Note the @angular/build:application builder — the modern esbuild-based builder that replaced the older webpack-based one. It is what powers the fast dev server and optimized production builds in current Angular versions.
The src folder
Everything you actually write lives under src. The three files at its root are the application’s entry points into the browser.
index.html
The single HTML page served to the browser. Angular is a single-page application, so this file rarely changes. The CLI injects the compiled script bundles automatically; your job is just to provide the root element the app mounts into.
<!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>
main.ts
main.ts is the JavaScript entry point. It boots the application by handing the root component and the application config to bootstrapApplication. There is no NgModule here — this is the standalone bootstrap path.
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
app.config.ts
This file replaces the old AppModule. It exports an ApplicationConfig whose providers array registers application-wide services and features such as the router, HTTP client, and zoneless or zone-based change detection. Functional providers like provideRouter and provideHttpClient make the configuration declarative and tree-shakable.
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),
],
};
app.component.ts and app.routes.ts
app.component.ts is the root component — a standalone component with its own template, styles, and imports. app.routes.ts exports the route table consumed by provideRouter.
import { Component, signal } 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 {
protected readonly title = signal('my-app');
}
import { Routes } from '@angular/router';
export const routes: Routes = [
// { path: 'about', loadComponent: () => import('./about/about.component').then((m) => m.AboutComponent) },
];
Warning: Don’t confuse
styles.css(global, project-wide styles atsrc/styles.css) with a component’sstyleUrl(scoped styles that only affect that component’s template). Global rules belong in the former; component-specific rules in the latter.
Best Practices
- Organize features into folders under
src/app(e.g.app/users/,app/orders/) rather than dumping every component in one directory. - Register cross-cutting providers in
app.config.ts, and use route-level providers for feature-scoped services. - Put static files in
public/so they’re copied as-is; reservesrcfor code the build process transforms. - Define budgets in
angular.jsonso the build warns when bundles grow beyond an acceptable size. - Prefer lazy-loaded routes with
loadComponent/loadChildreninapp.routes.tsto keep the initial bundle small. - Avoid editing compiled output in
dist/; treatangular.jsonand thetsconfigfiles as the place to change build behavior.