Angular Basics #2: Components and Template Syntax

8 min read

Last time, we looked at what Angular is and what its strengths are. From this post on, you start writing actual code. It’s not an exaggeration to say that learning Angular starts with the Component and ends with the component — components are the basic unit of every screen.

There are four things to do in this post.

  1. Install Node.js and Angular CLI
  2. Create your first project with ng new
  3. Understand the structure of a single component (@Component decorator)
  4. Create a new component and slot it into a parent component

Installing Node.js and Angular CLI #

To develop with Angular, you first need Node.js. The Angular CLI itself runs on Node.js, and the build/test tools all use the Node environment.

Visit nodejs.org and install the LTS (Long Term Support) version. At the time of writing, the LTS is the Node 22 line. On Mac, you can also install via Homebrew.

Install Node.js (Homebrew)
brew install node

Once installation is done, open a new terminal and check the versions.

Check versions
node -v
npm -v

If Node and npm versions print out, you’re good. Now install Angular CLI, the standard Angular tool, globally.

Install Angular CLI
npm install -g @angular/cli

-g is short for “global.” It installs the package system-wide so you can use the ng command from any folder. After installation, verify with:

Check Angular CLI version
ng version

If you see the Angular CLI version along with an ASCII-art Angular logo, you’re set. This series targets Angular v17 or later (the point where Standalone Components are the default).

Note
On Mac, if you get a permissions error when running ng version, your npm global path config may have an issue. In most cases, installing Node via nvm or Homebrew solves it.

Creating your first project #

Move to the location you want (e.g., ~/projects or Desktop) and run:

Create a new project
ng new my-app

my-app is the project folder name — feel free to use a different one. When you run the command, you’ll get a couple of questions.

  1. Which stylesheet format would you like to use? — picks the style format. Choose CSS (the most neutral choice for beginners; pick SCSS, Sass, or Less if you’re already comfortable with them).
  2. Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? — asks whether to enable SSR/SSG. Answer No for the basics. You can add it as an option later when you need it.

Once the questions are done, Angular CLI creates the folder and installs dependencies automatically. It can take a while, so feel free to grab a tea.

Once installation is done, take a look at the folder structure.

my-app/
my-app/
├── node_modules/        ← installed libraries
├── public/              ← static files (favicon, etc.)
├── src/                 ← the code we'll write
│   ├── app/
│   │   ├── app.component.ts    ← root component (TS)
│   │   ├── app.component.html  ← root component (template)
│   │   ├── app.component.css   ← root component (styles)
│   │   ├── app.config.ts       ← app config (routing, providers, etc.)
│   │   └── app.routes.ts       ← routing definitions
│   ├── index.html       ← HTML where the app is injected
│   ├── main.ts          ← app entry point (bootstrap)
│   └── styles.css       ← global styles
├── angular.json         ← Angular CLI config
├── package.json         ← project info and dependencies
└── tsconfig.json        ← TypeScript config

The most important folder at this stage is src/app/. Every component you’ll create goes inside it.

Running the dev server #

Now move into the project folder and start the dev server.

Run the dev server
cd my-app
ng serve

If you see something like the following, it worked.

Output
  ➜  Local:   http://localhost:4200/
  ➜  press h + enter to show help

Open your browser at http://localhost:4200. If you see a welcome screen with the Angular logo, everything is fine.

In that state, change a bit of text in src/app/app.component.html and save. The browser updates immediately without a refresh. That’s HMR (Hot Module Replacement) — a dev feature that redraws only the changed part in real time.

Tip
To stop the dev server, press Ctrl + C in the terminal. To start again, run ng serve from the same folder. To change the port, pass an option like ng serve --port 4300.

The shape of a single component #

Now to the real point. An Angular component usually moves as a set of three files.

  • app.component.ts — the component’s class (TypeScript)
  • app.component.html — the template that draws the screen
  • app.component.css — styles applied only to this component

Open src/app/app.component.ts and you’ll see something like this.

src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'my-app';
}

It looks foreign at first, but break it down line by line and it’s surprisingly simple.

  • @Component({...}) — a decorator that declares “make this class an Angular component.” It attaches metadata to the AppComponent class below it.
  • selector: 'app-root' — the tag name to use when you reference this component in HTML. You’ll use it as <app-root></app-root>.
  • standalone: true — means this component is a Standalone Component. In the past, components had to be bundled into an NgModule, but in modern Angular components run independently without a module.
  • imports: [...] — where you pre-register other components or directives this component’s template will use. The example registers RouterOutlet, used for routing.
  • templateUrl / styleUrl — paths to the template and style files. For a short component, you can also write them inline as template: '<h1>...</h1>' instead of separate files.
  • export class AppComponent — the actual component body. Where data (class fields) and behavior (methods) live.

A simple analogy: @Component is “a label that describes what kind of screen piece this class is,” and the class below it is “the brain of that piece.”

Interpolation #

To display data defined on the class in the template, you use interpolation. Notice the field title = 'my-app' we created above. Let’s display its value on the screen.

Open src/app/app.component.html, clear all the contents, and write:

src/app/app.component.html
<h1>Hello, {{ title }}!</h1>
<p>This is my first Angular app.</p>

When you save, the browser shows “Hello, my-app!”. When you write a class property inside double curly braces ({{ }}), its value gets printed on the screen. That’s interpolation.

Curly braces can hold not just simple properties but simple expressions too.

src/app/app.component.html
<h1>Hello, {{ title.toUpperCase() }}!</h1>
<p>1 + 2 = {{ 1 + 2 }}</p>

That said, don’t put overly complex logic inside template expressions. For maintainability, push complex calculations into class methods or getters and call only the result in the template.

Creating a new component #

You can’t build a whole app with just the root component (AppComponent). The basic mindset of Angular (and every component-based framework) is to break the screen into small pieces, make each piece a separate component, and assemble them.

You create new components with Angular CLI.

Create a new component
ng generate component user-card

If typing the long form is annoying, there’s a short form too.

Create a new component (short form)
ng g c user-card

Running the command generates these files automatically inside src/app/user-card/.

user-card/
src/app/user-card/
├── user-card.component.ts
├── user-card.component.html
├── user-card.component.css
└── user-card.component.spec.ts   ← test file

Open user-card.component.ts and you’ll see code that looks almost the same as the AppComponent we saw earlier.

src/app/user-card/user-card.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-user-card',
  standalone: true,
  imports: [],
  templateUrl: './user-card.component.html',
  styleUrl: './user-card.component.css',
})
export class UserCardComponent {
  name = 'Curtis';
  role = 'Frontend Developer';
}

I added name and role fields myself. Now write the template.

src/app/user-card/user-card.component.html
<div class="card">
  <h2>{{ name }}</h2>
  <p>{{ role }}</p>
</div>

Add a touch of style too.

src/app/user-card/user-card.component.css
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px;
  width: 240px;
}

This component’s selector is app-user-card. So if you put <app-user-card></app-user-card> somewhere in HTML, this component is drawn there.

Assembling components #

Now let’s slot the UserCardComponent we built into the root component, AppComponent. Two steps are needed.

First, register the child component in the parent’s imports array. Standalone components don’t have an NgModule, so you must explicitly import the components you’ll use.

src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { UserCardComponent } from './user-card/user-card.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, UserCardComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'my-app';
}

Then use the child’s selector in the parent template.

src/app/app.component.html
<h1>Hello, {{ title }}!</h1>

<app-user-card></app-user-card>
<app-user-card></app-user-card>

Save, and you’ll see two UserCard cards drawn on the screen. Like Lego blocks, components can be slotted into other components’ templates and reused multiple times in different places.

Tip
“Why do I have to register it in imports every time?” Standalone components don’t have a giant registry like NgModule, so each component explicitly brings in only what it needs. It feels tedious at first, but per-component dependencies become clear, with big benefits for tree-shaking and bundle optimization.

Of course, the two cards currently show the same data, which is a limitation. To show a different person’s information per card, the parent needs to pass data to the children — and that’s the topic of the next post.

Recap #

In this post, we installed Node.js and Angular CLI, created our first project with ng new, and looked at the structure of the @Component decorator. We also displayed class data on the screen with interpolation ({{ }}) and used ng generate component to create a new component and slot it into a parent template.

Even with just this much, you should have a big-picture view of how an Angular app is assembled component by component. In the next post, “Angular Basics #3: Data Binding and Events,” you’ll dig into property binding ([name]="...") for passing data from parent to child, and event binding ((click)="...") for handling events like user clicks.

X