Skip to content
Angular ng getting-started 4 min read

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: true flag is no longer written into the decorator. If you see standalone: true everywhere, 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.

FilePurpose
angular.jsonWorkspace/build configuration consumed by the CLI: build targets, file replacements, budgets, and asset paths.
package.jsonnpm dependencies and the scripts (start, build, test) you invoke with npm run.
tsconfig.jsonBase TypeScript compiler options shared across the workspace.
tsconfig.app.jsonApplication-specific TS config that extends the base and lists app source files.
tsconfig.spec.jsonTS 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 at src/styles.css) with a component’s styleUrl (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; reserve src for code the build process transforms.
  • Define budgets in angular.json so the build warns when bundles grow beyond an acceptable size.
  • Prefer lazy-loaded routes with loadComponent/loadChildren in app.routes.ts to keep the initial bundle small.
  • Avoid editing compiled output in dist/; treat angular.json and the tsconfig files as the place to change build behavior.
Last updated June 14, 2026
Was this helpful?