Setting up a workspace with the Angular CLI

Create a workspace with the options that matter, understand the files the CLI generates, and know where configuration actually lives.

ng new and the options that matter

npm install -g @angular/cli
ng version

ng new billing-portal \
  --style=css \
  --ssr=false \
  --zoneless \
  --routing \
  --package-manager=pnpm \
  --skip-git=false

cd billing-portal
ng serve --open
ng build --configuration production
FlagEffectWhy it matters
--styleChooses the stylesheet languageHard to change later; the component metadata references the extension
--ssrAdds a server entry point and hydrationAdds complexity; enable it when you need rendering or SEO
--zonelessOmits Zone.js and enables signal-based change detectionSmaller bundle, better performance, but every async update must notify explicitly
--routingGenerates app.routes.tsYou can add routing later, but starting with it avoids a refactor
--standaloneComponents declare their own importsThe default in modern Angular; NgModules are now optional
--skip-testsNo spec filesOnly sensible when the project genuinely will not be tested
💡
Zoneless is a commitment rather than a toggle. Anything that updates state from outside Angular — a setTimeout, a WebSocket callback, a third-party library — must touch a signal or call markForCheck. Decide before you write the first component.

What the CLI generates

billing-portal/
  angular.json           build, serve and test targets per project
  package.json           dependencies and npm scripts
  tsconfig.json          base TypeScript config, strict by default
  tsconfig.app.json      app-specific compiler options
  src/
    main.ts              bootstrapApplication(AppComponent, appConfig)
    app/
      app.ts             the root standalone component
      app.config.ts      providers: router, http, etc.
      app.routes.ts      the route table
      app.html / app.css
    index.html
    styles.css
  public/                copied verbatim into the build output
// src/app/app.config.ts — the composition root
import { ApplicationConfig, provideBrowserGlobalErrorListeners,
         provideZonelessChangeDetection } from '@angular/core';
import { provideRouter, withComponentInputBinding, withViewTransitions } from '@angular/router';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './core/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideZonelessChangeDetection(),
    provideRouter(routes, withComponentInputBinding(), withViewTransitions()),
    provideHttpClient(withFetch(), withInterceptors([authInterceptor]))
  ]
};
  • main.ts should stay three lines: it bootstraps the root component with the app config and nothing else.
  • Testing and build settings live per project in angular.json; there is no hidden webpack file to edit and no ng eject.
  • The new build system is esbuild-based, so builds are dramatically faster but the output is not a webpack bundle — anything that patched webpack internals will not carry over.
  • public/ is copied as-is and referenced from the site root, which is where favicons and robots.txt belong.

ng generate and budgets

ng generate component invoices/invoice-list
ng g c shared/ui/stat-card --change-detection=OnPush --skip-tests
ng g s core/auth
ng g guard core/require-auth          # a functional CanActivateFn
ng g interceptor core/auth
ng g environment

ng g c invoices/invoice-list --dry-run  # print the plan, write nothing
ng config schematics.@schematics/angular:component.changeDetection OnPush
ng config cli.analytics false
// angular.json — budgets are the mechanism that keeps a bundle from creeping
{
  "configurations": {
    "production": {
      "budgets": [
        { "type": "initial", "maximumWarning": "400kB", "maximumError": "600kB" },
        { "type": "anyComponentStyle", "maximumWarning": "4kB", "maximumError": "8kB" },
        { "type": "bundle", "name": "vendor", "maximumWarning": "200kB" }
      ],
      "outputHashing": "all",
      "optimization": true,
      "sourceMap": false
    },
    "development": {
      "budgets": [
        { "type": "initial", "maximumWarning": "2MB", "maximumError": "4MB" }
      ]
    }
  }
}
CommandUse it forNote
ng serveLocal developmentHot module replacement is on by default
ng buildDeployment artefactAdd --configuration production for a real build
ng build --stats-jsonBundle analysisFeed stats.json to a bundle visualiser
ng testUnit testsRunner and builder are configurable
ng extract-i18nTranslation cataloguesOutputs XLIFF or JSON
ng updateFramework upgradesRuns migrations; commit first

Set strict in tsconfig.json on day one. Turning on strictTemplates later is a large refactor, and it is the check that catches the majority of real template bugs — a mistyped property or a wrong event payload.

FAQ

Standalone or NgModules?
Standalone. It is the default for new work, requires less ceremony, and makes dependencies explicit at the point of use. NgModule-based code still compiles, so an existing application can migrate one component at a time.
Should I enable server-side rendering from the start?
Only if you need it — search indexing of public pages, or a measurable first-contentful-paint problem on slow networks. SSR changes how you write code: no direct window access on the server, and every request must be careful about shared state.

Components and templates Server rendering, hydration and deployment

Last refreshed 2026-09-18.