Lesson 1 of 30
Angular isn't a library you assemble a stack around — it's a framework that already made most of the stack's decisions for you. That's the whole trade.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
React is a library: it renders components, and you bring your own router, your own state manager, your own HTTP client, your own form solution. Two React codebases at two different companies can look almost nothing alike once you get past JSX.
Angular is a framework: routing, dependency injection, forms, and an HTTP client all ship in the box, versioned together, tested together, and designed to talk to each other. You don't assemble Angular's stack — Angular hands you one, and your job is to work inside it. That's the real meaning of "framework vs library": it's not about size, it's about who's in control. A library is a tool you call. A framework calls you — it owns the bootstrap process, the render loop, the lifecycle, and you plug your code into the slots it defines. This is called inversion of control, and it's the single idea underneath everything else in this track.
Here's what "designed together" looks like in practice — three files from a freshly generated Angular app:
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: '', loadComponent: () => import('./home/home.component').then(m => m.HomeComponent) },
{ path: 'products/:id', loadComponent: () => import('./product/product.component').then(m => m.ProductComponent) },
];
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withFetch()),
],
};
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig);
Notice what's not here: no separate router library to configure against a separate HTTP client's interceptor system, no glue code reconciling two different dependency-injection containers. provideRouter and provideHttpClient both register into the same DI system Angular already runs — a route guard can inject() a service that itself injects the HTTP client, and none of it needs adapter code, because the router, the HTTP client, and dependency injection were built by the same team, in the same repo, against the same conventions. That's what "batteries included" buys you: the batteries fit the flashlight.
useState and fetch in an afternoon. Getting comfortable with Angular's DI, its template syntax, and its build tooling takes longer — but that cost is paid once, and it buys consistency across every Angular codebase you'll ever join, because they're all built on the same conventions.Every lesson after this one is really just a tour of one framework decision at a time — how components declare their own dependencies, how signals drive rendering, how the router and DI cooperate. If you've worked in React, the closest analogy is the difference between React's component-tree-plus-ecosystem model and a single cohesive platform: same underlying idea (a reactive UI driven by state), very different amount of structure imposed on you to get there.