Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to build a checkout page in Angular

Building a checkout page is a critical task in e-commerce that requires a perfect balance of user experience and data integrity.
As the creator of CoreUI and with over 25 years of software development experience, I’ve designed dozens of checkout flows that handle complex validation and state management.
In Angular, the most efficient way to manage a multi-field checkout process is by utilizing Reactive Forms combined with a modular component architecture.
This approach ensures your form is scalable, easy to test, and provides immediate feedback to the user.

Use Angular Reactive Forms to build a structured, validated checkout flow that integrates seamlessly with CoreUI components.

1. Defining the Form Model

The first step is to create a robust form structure using FormBuilder. We will group fields into logical sections like personal information, shipping address, and payment details.

import { Component, OnInit } from '@angular/core'
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'
import { CardModule, GridModule, FormModule, ButtonModule, ListGroupModule } from '@coreui/angular'

@Component({
  selector: 'app-checkout',
  standalone: true,
  imports: [ReactiveFormsModule, CardModule, GridModule, FormModule, ButtonModule, ListGroupModule],
  templateUrl: './checkout.component.html'
})
export class CheckoutComponent implements OnInit {
  checkoutForm!: FormGroup

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.checkoutForm = this.fb.group({
      personalInfo: this.fb.group({
        firstName: ['', [Validators.required, Validators.minLength(2)]],
        lastName: ['', [Validators.required]],
        email: ['', [Validators.required, Validators.email]]
      }),
      shipping: this.fb.group({
        address: ['', [Validators.required]],
        city: ['', [Validators.required]],
        zip: ['', [Validators.required, Validators.pattern('^[0-9]{5}$')]]
      }),
      payment: this.fb.group({
        method: ['cc', [Validators.required]],
        cardName: ['', [Validators.required]],
        cardNumber: ['', [Validators.required, Validators.pattern('^[0-9]{16}$')]],
        expiry: ['', [Validators.required]],
        cvv: ['', [Validators.required, Validators.maxLength(3)]]
      })
    })
  }

  onSubmit() {
    if (this.checkoutForm.valid) {
      console.log('Order Data:', this.checkoutForm.value)
    }
  }
}

2. Implementing the Layout with CoreUI Cards

Using CoreUI Cards, we can separate the checkout process into distinct visual sections. This reduces cognitive load for the customer.

<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()">
  <c-row>
    <c-col [md]="8">
      <!-- Shipping Section -->
      <c-card class="mb-4">
        <c-card-header>
          <strong>Shipping Information</strong>
        </c-card-header>
        <c-card-body formGroupName="shipping">
          <c-row class="mb-3">
            <c-col [md]="12">
              <label cLabel for="address">Street Address</label>
              <input cFormControl id="address" formControlName="address" placeholder="123 Main St" />
            </c-col>
          </c-row>
          <c-row>
            <c-col [md]="6">
              <label cLabel for="city">City</label>
              <input cFormControl id="city" formControlName="city" />
            </c-col>
            <c-col [md]="6">
              <label cLabel for="zip">Zip Code</label>
              <input cFormControl id="zip" formControlName="zip" />
            </c-col>
          </c-row>
        </c-card-body>
      </c-card>
    </c-col>
  </c-row>
</form>

3. Adding Payment Method Selection

For payment selection, we use CoreUI Checks and Radios to allow users to toggle between Credit Card, PayPal, or other methods.

<c-card class="mb-4">
  <c-card-header>
    <strong>Payment Method</strong>
  </c-card-header>
  <c-card-body formGroupName="payment">
    <c-form-check>
      <input cFormCheckInput type="radio" formControlName="method" id="cc" value="cc" />
      <label cFormCheckLabel for="cc">Credit Card</label>
    </c-form-check>
    <c-form-check>
      <input cFormCheckInput type="radio" formControlName="method" id="paypal" value="paypal" />
      <label cFormCheckLabel for="paypal">PayPal</label>
    </c-form-check>
    
    <div class="mt-4">
      <label cLabel for="cardName">Name on Card</label>
      <input cFormControl id="cardName" formControlName="cardName" />
      
      <c-row class="mt-3">
        <c-col [md]="8">
          <label cLabel for="cardNumber">Card Number</label>
          <input cFormControl id="cardNumber" formControlName="cardNumber" />
        </c-col>
        <c-col [md]="4">
          <label cLabel for="cvv">CVV</label>
          <input cFormControl id="cvv" formControlName="cvv" />
        </c-col>
      </c-row>
    </div>
  </c-card-body>
</c-card>

4. Building the Order Summary

An order summary is essential for trust. We can use a CoreUI List Group to display items and calculate the total.

<c-col [md]="4">
  <c-card>
    <c-card-header>
      <strong>Order Summary</strong>
    </c-card-header>
    <c-card-body>
      <ul cListGroup flush>
        <li cListGroupItem class="d-flex justify-content-between align-items-center">
          Product A <span>$49.99</span>
        </li>
        <li cListGroupItem class="d-flex justify-content-between align-items-center">
          Product B <span>$25.00</span>
        </li>
        <li cListGroupItem class="d-flex justify-content-between align-items-center border-top mt-2">
          <strong>Total</strong>
          <strong>$74.99</strong>
        </li>
      </ul>
      <button cButton color="primary" class="w-100 mt-3" type="submit" [disabled]="!checkoutForm.valid">
        Complete Purchase
      </button>
    </c-card-body>
  </c-card>
</c-col>

5. Integrating Form Validation Feedback

Angular Reactive Forms provide state classes that we can map to CoreUI Validation styles to show real-time errors.

// Add a helper method to check field status
isFieldInvalid(section: string, field: string): boolean {
  const control = this.checkoutForm.get(`${section}.${field}`)
  return !!(control && control.invalid && (control.dirty || control.touched))
}
<input 
  cFormControl 
  id="email" 
  formControlName="email" 
  [valid]="!isFieldInvalid('personalInfo', 'email')"
/>
<c-form-feedback *ngIf="isFieldInvalid('personalInfo', 'email')" [valid]="false">
  Please provide a valid email address.
</c-form-feedback>

6. Optional: Multi-step Checkout with Stepper (PRO)

For longer checkouts, the CoreUI Stepper improves the flow by breaking the form into steps (e.g., Shipping -> Payment -> Review). This is a PRO component available in the @coreui/angular-pro package.

<c-stepper>
  <c-stepper-header>
    <c-stepper-item>Shipping</c-stepper-item>
    <c-stepper-item>Payment</c-stepper-item>
    <c-stepper-item>Confirm</c-stepper-item>
  </c-stepper-header>
  <c-stepper-content>
    <!-- Step content goes here -->
  </c-stepper-content>
</c-stepper>

Explanation

In this implementation, we use Angular’s FormBuilder to create a nested FormGroup. This allows us to separate concerns into personalInfo, shipping, and payment. The [formGroup] directive in the template binds our logic to the UI. We use CoreUI’s grid system (c-row, c-col) to create a responsive two-column layout where the form sits on the left and the order summary stays pinned to the right (on larger screens). Each input uses the cFormControl directive, which automatically applies professional styling and integrates with Angular’s validation state. The “Complete Purchase” button is bound to the form’s validity, preventing submission if requirements aren’t met.

Best Practice Note:

Always ensure you handle sensitive payment data securely. In a production environment, you should never send raw credit card details to your own server; instead, use a provider like Stripe to tokenize the data. For high-conversion checkouts, we recommend using the CoreUI Angular Dashboard Template which includes pre-built eCommerce pages. If you are just starting your project, you might want to check how to create a new angular project first. Additionally, make sure to format numbers as currency in your summary for a professional finish.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to loop inside React JSX
How to loop inside React JSX

How to loop through a 2D array in JavaScript
How to loop through a 2D array in JavaScript

What is globalThis in JavaScript?
What is globalThis in JavaScript?

Dealing with Sass Deprecation Warnings in Angular 19
Dealing with Sass Deprecation Warnings in Angular 19

Answers by CoreUI Core Team