How to Set Up Angular Project

Introduction Angular remains one of the most powerful and widely adopted frameworks for building dynamic, scalable web applications. With its robust architecture, dependency injection system, and strong typing via TypeScript, Angular empowers developers to create enterprise-grade applications. However, setting up an Angular project isn’t just about running ng new. A poorly configured project can l

Oct 25, 2025 - 13:38
Oct 25, 2025 - 13:38
 0

Introduction

Angular remains one of the most powerful and widely adopted frameworks for building dynamic, scalable web applications. With its robust architecture, dependency injection system, and strong typing via TypeScript, Angular empowers developers to create enterprise-grade applications. However, setting up an Angular project isnt just about running ng new. A poorly configured project can lead to performance bottlenecks, security vulnerabilities, maintenance nightmares, and team friction. The difference between a project that lasts years and one that collapses under its own weight often comes down to how it was set up from day one.

This guide presents the top 10 proven, industry-trusted methods to set up an Angular project that you can rely on not just today, but for the long term. These arent quick hacks or trendy workarounds. Each step is grounded in real-world experience, Angular best practices, and the collective wisdom of senior developers and architecture teams at Fortune 500 companies and leading tech startups. Whether you're a solo developer or leading a team of ten, these practices will ensure your Angular project is secure, scalable, maintainable, and production-ready from the first commit.

Why Trust Matters

Trust in software development isnt a luxury its a necessity. When you set up an Angular project, youre not just installing dependencies or configuring a build tool. Youre laying the foundation for every feature, every bug fix, and every future developer who will touch the codebase. A project built without trust principles becomes a technical debt time bomb.

Consider this: a project with inconsistent folder structures, undocumented configurations, or unvetted third-party libraries may appear functional during development. But when a critical security patch is released, or a new team member joins, or performance degrades under load, the cracks begin to show. Without trust, you lose predictability. Without trust, you lose velocity. Without trust, you lose confidence in your code, your team, and your product.

Trust is built through consistency, documentation, automation, and adherence to standards. Its about choosing tools and patterns that have been battle-tested across multiple projects and teams. Its about knowing that when you run ng build --prod, the output is optimized, secure, and reproducible. Its about knowing that when someone else opens your project, they can understand it within minutes not days.

In this guide, we focus on the 10 most critical setup decisions that directly impact trust. Each one is selected because it either prevents common pitfalls, enforces best practices, or significantly improves long-term maintainability. Well walk through each in detail, explaining why it matters, how to implement it, and what happens when you skip it.

Top 10 How to Set Up Angular Project

1. Use the Official Angular CLI with Latest Stable Version

The Angular CLI is the official, supported toolchain for creating, building, and managing Angular applications. Its maintained by the Angular team at Google and is updated in sync with Angular releases. Never bypass it by manually configuring Webpack, Rollup, or other bundlers unless you have a very specific, well-justified reason and even then, document it exhaustively.

Always start your project with the latest stable version of Angular CLI. As of 2024, thats Angular 17+ with CLI version 17.x. Newer versions include performance improvements, better tree-shaking, improved error messages, and support for modern JavaScript features like ES2023 and native module support.

To install the latest CLI globally:

npm install -g @angular/cli@latest

Then create your project:

ng new my-trusted-project --strict --routing --style=scss

The flags used here are intentional:

  • --strict enables TypeScript strict mode, preventing common type errors and improving code reliability.
  • --routing sets up a modular routing system from the start, essential for any non-trivial app.
  • --style=scss uses SCSS for styles the industry standard for scalable CSS architecture.

Skipping strict mode or routing might save a few minutes during setup, but it will cost you hours in debugging later. Strict mode catches null/undefined errors at compile time. Routing ensures your app scales cleanly as features grow. SCSS supports variables, mixins, and nested rules critical for maintaining consistent design systems.

2. Enforce TypeScript Strict Mode and Configure tsconfig.json

TypeScript is Angulars backbone. Ignoring its type system is like driving a race car with the parking brake on. The default tsconfig.json generated by Angular CLI is a good start, but it must be hardened for production-grade trust.

Ensure your tsconfig.json includes these critical settings:

{

"compileOnSave": false,

"compilerOptions": {

"strict": true,

"noImplicitAny": true,

"noImplicitThis": true,

"alwaysStrict": true,

"strictNullChecks": true,

"strictFunctionTypes": true,

"strictBindCallApply": true,

"noImplicitThis": true,

"noImplicitReturns": true,

"noFallthroughCasesInSwitch": true,

"esModuleInterop": true,

"skipLibCheck": true,

"forceConsistentCasingInFileNames": true,

"target": "ES2022",

"module": "ES2022",

"moduleResolution": "Node",

"lib": ["ES2022", "DOM", "DOM.Iterable"]

},

"angularCompilerOptions": {

"enableI18nLegacyMessageIdFormat": false,

"strictInjectionParameters": true,

"strictInputAccessModifiers": true,

"strictTemplates": true

}

}

These settings eliminate entire classes of runtime bugs. For example:

  • strictNullChecks prevents null/undefined access errors a leading cause of crashes in production.
  • noImplicitReturns ensures every code path returns a value, avoiding undefined behavior.
  • strictTemplates in Angular Compiler Options validates template bindings at compile time, catching typos and type mismatches before deployment.

Additionally, enable forceConsistentCasingInFileNames to prevent case-sensitive file import errors across different operating systems (Windows vs. macOS/Linux). This is often overlooked but causes real issues in team environments.

Never ignore TypeScript warnings. Treat them as errors during development. In your package.json, add a script:

"scripts": {

"lint": "ng lint",

"type-check": "tsc --noEmit",

"build": "ng build --configuration production"

}

Run npm run type-check in your CI pipeline. A project that compiles cleanly with strict mode is a project you can trust.

3. Implement a Modular Folder Structure with Feature-Based Organization

One of the most common causes of Angular project decay is a disorganized folder structure. Avoid the flat file anti-pattern where everything lives in src/app/. Instead, adopt a feature-based structure that groups related code together.

Heres the trusted structure:

src/

??? app/

? ??? core/

? ? ??? interceptors/

? ? ??? guards/

? ? ??? services/

? ? ??? utils/

? ??? shared/

? ? ??? components/

? ? ??? directives/

? ? ??? pipes/

? ? ??? models/

? ??? features/

? ? ??? auth/

? ? ? ??? components/

? ? ? ??? services/

? ? ? ??? guards/

? ? ? ??? models/

? ? ? ??? auth.module.ts

? ? ??? dashboard/

? ? ? ??? components/

? ? ? ??? services/

? ? ? ??? guards/

? ? ? ??? dashboard.module.ts

? ? ??? users/

? ? ??? components/

? ? ??? services/

? ? ??? guards/

? ? ??? users.module.ts

? ??? app-routing.module.ts

? ??? app.module.ts

??? assets/

??? environments/

??? index.html

??? main.ts

Key principles:

  • Core: Contains singleton services, interceptors, guards, and utilities that are app-wide and should be imported only once.
  • Shared: Reusable components, directives, and pipes used across multiple features. These are pure UI components with no business logic.
  • Features: Each major feature (auth, dashboard, users) has its own module, components, services, guards, and models. This enables lazy loading and clear boundaries.

This structure makes onboarding faster, debugging easier, and testing more isolated. New developers can locate code by feature, not by file type. It also enables lazy loading a critical performance optimization because each feature module can be loaded on demand.

Never place services inside feature modules unless they are feature-specific. Shared services belong in core/. Components that are reused across features belong in shared/. This separation prevents circular dependencies and ensures clean encapsulation.

4. Configure Lazy Loading for Feature Modules

Lazy loading is not optional for scalable Angular applications. It reduces initial bundle size, improves load times, and enhances perceived performance especially on mobile and low-bandwidth connections.

Start by ensuring your root app-routing.module.ts uses lazy-loaded routes:

const routes: Routes = [

{

path: '',

loadChildren: () => import('./features/home/home.module').then(m => m.HomeModule)

},

{

path: 'auth',

loadChildren: () => import('./features/auth/auth.module').then(m => m.AuthModule)

},

{

path: 'dashboard',

loadChildren: () => import('./features/dashboard/dashboard.module').then(m => m.DashboardModule)

},

{

path: '**',

redirectTo: ''

}

];

Each feature module should be self-contained and declare its own routes:

// auth.module.ts

@NgModule({

declarations: [LoginComponent, RegisterComponent],

imports: [

CommonModule,

RouterModule.forChild([

{ path: 'login', component: LoginComponent },

{ path: 'register', component: RegisterComponent },

{ path: '', redirectTo: 'login', pathMatch: 'full' }

])

],

exports: [RouterModule]

})

export class AuthModule { }

Never use eager loading (importing modules directly in AppModule) for non-core features. Eager loading bloats your initial bundle and defeats the purpose of modular architecture.

Verify lazy loading is working using Chrome DevTools. Go to the Network tab, reload the app, and observe when each chunk loads. You should see separate JavaScript files (e.g., features-auth-module.js) loading only when the route is navigated to.

Lazy loading also improves maintainability. Teams can work on separate feature modules without stepping on each others toes. It reduces compile times and makes unit testing more focused.

5. Set Up a Robust HTTP Interceptor Chain

HTTP communication is the lifeblood of any Angular application. Without proper interception, you risk leaking sensitive data, failing to handle errors gracefully, or duplicating logic across services.

Build a centralized interceptor chain in core/interceptors/. At minimum, include:

  • Auth Interceptor: Attaches JWT tokens to outgoing requests.
  • Error Interceptor: Handles 401, 403, 500, and network errors uniformly.
  • Loading Interceptor: Shows/hides a global loading spinner.
  • Cache Interceptor (optional): Caches GET requests to reduce server load.

Example auth interceptor:

@Injectable()

export class AuthInterceptor implements HttpInterceptor {

constructor(private authService: AuthService) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

const token = this.authService.getToken();

if (token) {

req = req.clone({

setHeaders: {

Authorization: Bearer ${token}

}

});

}

return next.handle(req);

}

}

Register interceptors in core/core.module.ts:

@NgModule({

providers: [

{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },

{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },

{ provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true }

]

})

export class CoreModule { }

Always use multi: true to chain interceptors. They execute in the order they are registered.

Never hardcode API endpoints or headers in individual services. All HTTP configuration must flow through interceptors. This ensures consistency and makes it easy to change authentication schemes, base URLs, or headers globally without touching 50 service files.

Also, implement proper error handling. For example, if a 401 response is received, redirect to login and clear the token. If a 500 occurs, log it to an error reporting service (like Sentry) and display a user-friendly message. This transforms raw HTTP failures into a controlled user experience.

6. Configure Environment-Specific Settings with Angular Environments

Every Angular project must support multiple environments: development, staging, QA, and production. These environments differ in API endpoints, feature flags, logging levels, and third-party keys.

Use Angulars built-in environment system. Create files in src/environments/:

  • environment.ts Development
  • environment.staging.ts Staging
  • environment.qa.ts QA
  • environment.prod.ts Production

Each file exports an object with environment-specific values:

// environment.prod.ts

export const environment = {

production: true,

apiUrl: 'https://api.yourcompany.com/v1',

googleAnalyticsId: 'G-XXXXXXX',

sentryDsn: 'https://xxx@sentry.io/xxx',

featureFlags: {

newCheckout: true,

darkMode: false

}

};

In your code, import and use it:

import { environment } from 'src/environments/environment';

@Injectable()

export class ApiService {

private readonly baseUrl = environment.apiUrl;

constructor(private http: HttpClient) {}

getData() {

return this.http.get<any>(${this.baseUrl}/data);

}

}

Configure the build to use the correct environment in angular.json:

"configurations": {

"production": {

"fileReplacements": [

{

"replace": "src/environments/environment.ts",

"with": "src/environments/environment.prod.ts"

}

],

"optimization": true,

"outputHashing": "all",

"sourceMap": false,

"namedChunks": false,

"extractLicenses": true,

"vendorChunk": false,

"buildOptimizer": true,

"budgets": [

{

"type": "initial",

"maximumWarning": "2mb",

"maximumError": "5mb"

}

]

},

"staging": {

"fileReplacements": [

{

"replace": "src/environments/environment.ts",

"with": "src/environments/environment.staging.ts"

}

],

"optimization": true,

"outputHashing": "all",

"sourceMap": false,

"namedChunks": false,

"extractLicenses": true,

"vendorChunk": false,

"buildOptimizer": true

}

}

Build with:

ng build --configuration=staging

Environment configuration is not a nicety its a security and operational necessity. Hardcoding API keys or leaving debug logging enabled in production can lead to data breaches or performance degradation. Always validate your environment files are excluded from version control if they contain secrets. Use .env files and secrets management tools for sensitive data.

7. Integrate a Linter and Formatter (ESLint + Prettier)

Code style consistency is a silent productivity multiplier. When every developer writes code the same way, reviews become faster, bugs are easier to spot, and onboarding is smoother.

Angular CLI uses TSLint by default, but TSLint is deprecated. Migrate to ESLint + Prettier the modern, community-standard duo.

Install the required packages:

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier

Create a .eslintrc.json:

{

"root": true,

"ignorePatterns": ["projects/**/*"],

"overrides": [

{

"files": ["*.ts"],

"extends": [

"plugin:@angular-eslint/recommended",

"plugin:@typescript-eslint/recommended",

"plugin:@typescript-eslint/recommended-requiring-type-checking",

"eslint:recommended",

"prettier"

],

"parser": "@typescript-eslint/parser",

"parserOptions": {

"ecmaVersion": 2022,

"sourceType": "module",

"project": ["tsconfig.json"]

},

"plugins": ["@typescript-eslint", "prettier"],

"rules": {

"@typescript-eslint/no-unused-vars": "error",

"@typescript-eslint/no-explicit-any": "warn",

"prettier/prettier": "error"

}

},

{

"files": ["*.html"],

"extends": ["plugin:@angular-eslint/template/recommended"],

"rules": {}

}

]

}

Create a .prettierrc:

{

"semi": true,

"trailingComma": "es5",

"singleQuote": true,

"printWidth": 120,

"tabWidth": 2,

"useTabs": false

}

Add scripts to package.json:

"scripts": {
"lint": "eslint \"src//*.ts\" \"src//*.html\"",
"lint:fix": "eslint \"src//*.ts\" \"src//*.html\" --fix",
"format": "prettier --write \"src//*.ts\" \"src//*.html\" \"src/**/*.scss\""

}

Integrate with your IDE to auto-format on save. Most editors (VS Code, WebStorm) support ESLint and Prettier extensions.

Run linting in your CI pipeline. Fail the build if linting fails. This ensures code quality is non-negotiable.

Consistent formatting isnt about personal preference its about eliminating cognitive friction. When code looks the same everywhere, you focus on logic, not indentation.

8. Implement Comprehensive Testing: Unit, Integration, and E2E

A project you can trust is a project that is tested. Untested code is a liability. Angular provides excellent tooling for testing use it.

Structure your tests in three layers:

  1. Unit Tests Test individual components, services, and pipes in isolation using Jasmine and Karma.
  2. Integration Tests Test how components interact with services and directives using TestBed and Angulars testing utilities.
  3. E2E Tests Test user workflows from the browser using Cypress or Protractor (deprecated).

For unit and integration tests, use Angulars built-in TestBed:

// user.service.spec.ts

describe('UserService', () => {

let service: UserService;

let httpTestingController: HttpTestingController;

beforeEach(() => {

TestBed.configureTestingModule({

providers: [UserService],

imports: [HttpClientTestingModule]

});

service = TestBed.inject(UserService);

httpTestingController = TestBed.inject(HttpTestingController);

});

it('should fetch user data', () => {

const mockUser = { id: 1, name: 'John' };

service.getUser(1).subscribe(user => {

expect(user).toEqual(mockUser);

});

const req = httpTestingController.expectOne('/api/users/1');

expect(req.request.method).toBe('GET');

req.flush(mockUser);

httpTestingController.verify();

});

});

For E2E, use Cypress its modern, fast, and has excellent debugging tools:

// cypress/e2e/login.cy.ts

describe('Login Flow', () => {

it('should login successfully', () => {

cy.visit('/auth/login');

cy.get('[data-cy=email]').type('test@example.com');

cy.get('[data-cy=password]').type('password123');

cy.get('[data-cy=submit]').click();

cy.url().should('include', '/dashboard');

cy.get('[data-cy=user-name]').should('contain', 'John');

});

});

Use data-cy attributes instead of CSS selectors for reliability. Never rely on text content or brittle class names.

Set a minimum coverage threshold in karma.conf.js or via ng test --code-coverage and enforce it in CI. Aim for 80%+ coverage on core logic. Dont obsess over 100% focus on critical paths: authentication, data fetching, form validation, routing.

Testing isnt optional. Its the safety net that lets you refactor confidently, deploy frequently, and sleep at night.

9. Optimize Bundle Size and Performance with Build Optimizations

Performance isnt an afterthought its a core feature. A slow app loses users. Angulars build system provides powerful tools to optimize bundle size and runtime performance.

In angular.json, ensure production configuration includes:

  • optimization: true Enables minification, tree-shaking, and dead code elimination.
  • outputHashing: 'all' Adds content hashes to filenames for better caching.
  • sourceMap: false Prevents exposing source code in production.
  • buildOptimizer: true Removes Angular decorators and metadata not needed at runtime.
  • vendorChunk: false Merges vendor code into main bundle for fewer HTTP requests.
  • namedChunks: false Uses numeric chunk names for smaller filenames.

Also, enable Angulars built-in performance budget:

"budgets": [

{

"type": "initial",

"maximumWarning": "2mb",

"maximumError": "5mb"

},

{

"type": "anyComponentStyle",

"maximumWarning": "6kb",

"maximumError": "10kb"

}

]

Use ng build --stats-json to generate a bundle analysis report:

npx webpack-bundle-analyzer dist/my-app/stats.json

This reveals which libraries are bloating your bundle. Common culprits: moment.js, lodash, fullcalendar. Replace them with lightweight alternatives like date-fns, lodash-es, or fullcalendar-scheduler.

Use lazy loading (covered earlier) to reduce initial load. Preload critical routes:

// app-routing.module.ts

@NgModule({

imports: [RouterModule.forRoot(routes, {

preloadingStrategy: PreloadAllModules

})],

exports: [RouterModule]

})

PreloadAllModules loads lazy modules in the background after initial load improving perceived performance for next-page navigation.

Use Angulars ChangeDetectionStrategy.OnPush in components that receive data via @Input(). This reduces unnecessary change detection cycles and improves rendering performance.

Always test performance on a real device, not just your high-end laptop. Use Lighthouse in Chrome DevTools to audit performance, accessibility, and SEO. Aim for scores above 90.

10. Automate CI/CD with GitHub Actions or GitLab CI

A project you can trust is a project that deploys reliably. Manual deployments are error-prone. Automation ensures every build is identical, tested, and validated before reaching users.

Set up a CI/CD pipeline that runs on every push to main or pull request. The pipeline should:

  1. Install dependencies
  2. Run linting
  3. Run unit and integration tests
  4. Run E2E tests
  5. Build for production
  6. Deploy to staging
  7. Notify team of success/failure

Heres a sample GitHub Actions workflow (.github/workflows/ci.yml):

name: CI Pipeline

on:

push:

branches: [ main ]

pull_request:

branches: [ main ]

jobs:

test-and-deploy:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- name: Setup Node.js

uses: actions/setup-node@v4

with:

node-version: '20'

- name: Install dependencies

run: npm ci

- name: Run lint

run: npm run lint

- name: Run unit tests

run: npm run test -- --watch=false --code-coverage

- name: Run E2E tests

run: npm run e2e -- --headless

- name: Build for production

run: npm run build -- --configuration=production

- name: Deploy to Staging

uses: peaceiris/actions-gh-pages@v3

with:

github_token: ${{ secrets.GITHUB_TOKEN }}

publish_dir: ./dist/my-app

publish_branch: gh-pages

commit_message: Deploy to staging

- name: Notify Slack

uses: 8398a7/action-slack@v3

with:

status: ${{ job.status }} channel: '

dev-alerts'

webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}

For production deployment, use a separate workflow triggered by Git tags or manual approval. Deploy to Netlify, Vercel, or AWS S3 + CloudFront.

Automated testing and deployment eliminate human error. They ensure that the code that passes tests in development is the exact same code that goes to production. This is the foundation of trust.

Comparison Table

Practice Without It With It Impact Level
Use Angular CLI with Strict Mode Runtime errors, inconsistent configs, hard to debug Compile-time safety, standardized setup, fewer bugs High
TypeScript Strict Mode Null/undefined crashes, vague type errors Strong typing, early bug detection, better IDE support High
Feature-Based Folder Structure Code chaos, hard to find files, circular dependencies Clear boundaries, easy navigation, supports lazy loading High
Lazy Loading Large initial bundle, slow first load, poor UX Faster load times, better performance, scalable architecture High
HTTP Interceptors Duplicated auth logic, inconsistent error handling Centralized, reusable, maintainable HTTP handling High
Environment Configs Hardcoded secrets, staging bugs, security risks Secure, environment-aware deployments High
ESLint + Prettier Code style conflicts, wasted review time Consistent code, faster reviews, automated formatting Medium
Comprehensive Testing Unreliable deployments, regression bugs, fear of change Confident refactoring, fewer production incidents High
Build Optimization Slow app, high bounce rate, poor Lighthouse score Fast, optimized, SEO-friendly application High
CI/CD Automation Manual errors, inconsistent builds, deployment delays Reliable, repeatable, traceable deployments High

FAQs

Can I skip strict mode to speed up development?

No. Skipping strict mode introduces subtle bugs that are hard to detect until production. The compile-time safety it provides saves far more time than it costs during setup. Use strict mode from day one.

Should I use NgRx or Angular Services for state management?

Start with Angular services and RxJS for simple state. Only introduce NgRx if you have complex, cross-component state interactions that require time-travel debugging, middleware, or persistent state. Most apps dont need it.

Is SCSS better than CSS or LESS?

SCSS is the industry standard for Angular. Its fully compatible with Angulars component styles, supports nesting and variables, and has broad tooling support. Avoid plain CSS for large apps it doesnt scale.

How do I handle third-party libraries safely?

Always audit dependencies using npm audit or npm install -g npm-audit-resolver. Prefer smaller, actively maintained libraries. Avoid libraries with high vulnerability scores or abandoned repositories. Use BundlePhobia to check bundle size impact before adding.

What if my team resists these practices?

Start small. Implement one practice at a time like ESLint + Prettier and show the benefits: fewer review comments, faster onboarding, fewer bugs. Let results speak. Document the why behind each practice so the team understands the long-term value.

Can I use Angular without TypeScript?

Technically yes, but its strongly discouraged. Angular is designed around TypeScript. Without it, you lose type safety, IDE support, and maintainability. Youre essentially using a framework without its core strength.

How often should I update Angular?

Angular releases a new major version every 6 months. Plan to update every 612 months. Use ng update to automate migrations. Delaying updates increases technical debt and security risk.

Do I need to use Docker for Angular deployment?

Not necessarily. Angular is a static frontend. Deploying to S3, Netlify, or Vercel is simpler and more cost-effective. Use Docker only if youre bundling it with a backend or need container orchestration.

Conclusion

Setting up an Angular project you can trust isnt about following a checklist its about cultivating a mindset of quality, consistency, and foresight. Each of the 10 practices outlined in this guide has been battle-tested across thousands of production applications. They are not suggestions. They are the foundation of reliable, scalable, and maintainable Angular development.

When you implement strict TypeScript, lazy loading, feature-based architecture, and automated testing, youre not just building an app youre building confidence. Confidence that your code will work tomorrow. Confidence that your team can evolve it without fear. Confidence that your users will have a fast, secure, and seamless experience.

There is no shortcut to trust. Its earned through discipline, attention to detail, and a commitment to long-term maintainability over short-term convenience. The time you invest today in proper setup will save you weeks or months of debugging, refactoring, and firefighting down the road.

Start with one practice. Then another. Build momentum. Make these standards part of your teams DNA. The result wont just be a better Angular project it will be a project you can be proud of, and one that stands the test of time.