// Tutorial //

Integrating Stripe in Angular With Stripe Elements

Published on November 8, 2017
Default avatar
By Alligator.io
Developer and author at DigitalOcean.
Integrating Stripe in Angular With Stripe Elements

This tutorial is out of date and no longer maintained.


Stripe recently released Stripe Elements, a set of UI elements that make it easy to build a custom checkout flow, complete with real-time validation and autocomplete support. In this post, we’ll go over the basics of integrating Stripe and Stripe Elements on the frontend with Angular.

We’ll create a simple payment form that gets a token from the Stripe API. Stripe’s documentation for Elements is for integrating using vanilla JavaScript, but here we’ll modify this implementation slightly to integrate with an Angular template-driven form.

Setting Up the Project

First, in your project’s index.html file, you’ll want to add and initialize Stripe.js as well as initialize Stripe Elements:


  <script src="https://js.stripe.com/v3/"></script>
  <script type="text/javascript">
    var stripe = Stripe('pk_test_XXXXXXXXXXXXXXXXX'); // use your test publishable key
    var elements = stripe.elements();

Warning: Remember to change to your live publishable key once your app goes to production.

Since Stripe.js is added outside the scope of the project and doesn’t have typings, TypeScript would normally complain when trying to access stripe or elements. To fix this, we’ll add two declarations to the project’s typings.d.ts file:

// ...

declare var stripe: any;
declare var elements: any;

We’ll be using Angular’s template-driven forms for our simple payment form, so we also have to import the FormsModule in our app or feature module:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import {FormsModule} from '@angular/forms';

  declarations: [
  imports: [
  providers: [],
  bootstrap: [AppComponent]
export class AppModule { }

Building the Form

The template markup for a basic checkout flow is as simple as it gets:

<form #checkout="ngForm" (ngSubmit)="onSubmit(checkout)" class="checkout">
  <div class="form-row">
    <label for="card-info">Card Info</label>
    <div id="card-info" #cardInfo></div>

    <div id="card-errors" role="alert" *ngIf="error">{{ error }}</div>

  <button type="submit">Pay $777</button>

The #card-info element will be the container for the Stripe Elements, and we also created a container div to display error messages, if any.

The fun part starts when we hook everything up in the component class. Here’s the code to make our example work, with some interesting parts highlighted:

import {
} from '@angular/core';

import { NgForm } from '@angular/forms';

@Component({ ... })
export class AppComponent implements AfterViewInit, OnDestroy {
  @ViewChild('cardInfo') cardInfo: ElementRef;

  card: any;
  cardHandler = this.onChange.bind(this);
  error: string;

  constructor(private cd: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.card = elements.create('card');

    this.card.addEventListener('change', this.cardHandler);

  ngOnDestroy() {
    this.card.removeEventListener('change', this.cardHandler);

  onChange({ error }) {
    if (error) {
      this.error = error.message;
    } else {
      this.error = null;

  async onSubmit(form: NgForm) {
    const { token, error } = await stripe.createToken(this.card);

    if (error) {
      console.log('Something is wrong:', error);
    } else {
      console.log('Success!', token);
      // ...send the token to the your backend to process the charge

There may seem to be a lot doing on at first glance, but it’s all really straightforward. Here are a few things to note:

  • We get access to the container for the card element using the ViewChild decorator.
  • We bind our onChange method to the this of the class and save the new reference as cardHandler. This reference is used to add an event listener when the card element is created and remove it in the OnDestroy hook.
  • We initialize the card element in the AfterViewInit lifecycle hook, to ensure that our container element is available.
  • We make use of ChangeDetectorRef to manually instruct Angular to run a change detection cycle in the onChange method.
  • Our form submission method, onSubmit, is an async function that awaits for Stripe’s createToken promise to resolve.
  • Once we get a valid token back from Stripe, the token should be sent to your backend or to a cloud function to process the charge.
  • In the OnDestroy we clean up by removing the change event listener and destroying the card element.

Our example is really barebones, but in a real app, you’ll want to also implement a simple boolean flag that prevents the user from submitting the form multiple times in a row. For example, the submit button could be replaced by a loading indicator from the time the form is submitted up until your backend sends a success message indicating that the charge was processed. In that case, you’d redirect the user to something like a confirmation page.

Note: To test things out, use card number 4242 4242 4242 4242 with any expiration date in the future, any 3-digit number for the CVC, and any valid zip code.

Customizing Styles

We have a simple checkout form working, but it looks pretty dull. Let’s add a touch of styles. First, let’s style our form and submit button:

form.checkout {
  max-width: 500px;
  margin: 2rem auto;
  text-align: center;
  border: 2px solid #eee;
  border-radius: 8px;
  padding: 1rem 2rem;
  background: white;

  font-family: monospace;
  color: #525252;
  font-size: 1.1rem;

form.checkout button {
  padding: 0.5rem 1rem;
  color: white;
  background: coral;
  border: none;
  border-radius: 4px;
  margin-top: 1rem;

form.checkout button:active {
  background: rgb(165, 76, 43);

The Stripe card element itself can be styled using selectors like .StripeElement, .StripeElement--focus and .StripeElement--invalid. There are a few ready-made theme examples available, but here we’ll just add the default style provided by Stripe:


.StripeElement {
  margin: 1rem 0 1rem;
  background-color: white;
  padding: 8px 12px;
  border-radius: 4px;
  border: 1px solid transparent;
  box-shadow: 0 1px 3px 0 #e6ebf1;
  -webkit-transition: box-shadow 150ms ease;
  transition: box-shadow 150ms ease;

.StripeElement--focus {
  box-shadow: 0 1px 3px 0 #cfd7df;

.StripeElement--invalid {
  border-color: #fa755a;

.StripeElement--webkit-autofill {
  background-color: #fefde5 !important;

Some basic styles can also be passed-in with an option object as a second argument to the create method:

app.component.ts (partial)
ngAfterViewInit() {
  const style = {
    base: {
      lineHeight: '24px',
      fontFamily: 'monospace',
      fontSmoothing: 'antialiased',
      fontSize: '19px',
      '::placeholder': {
        color: 'purple'

  this.card = elements.create('card', { style });

  this.card.addEventListener('change', this.cardHandler);

You can refer to the options API reference for a list of all the possible style configuration options.

Example of Stripe Elements in Angular

Saving Additional Fields

Our checkout form is all well and good, but what if we also want to save some additional data for the customer in Stripe? It’s as simple as passing a second argument to createToken with any extra field.

Here for example we also send the email address for the customer:

async onSubmit(form: NgForm) {
  const { token, error } = await stripe.createToken(this.card, {
    email: this.emailAddress

  // ...


In this post, you created a form using the Stripe API and Stripe Elements with an Angular template-driven form.

Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?

The Pay Button is missing from the document also many unwanted scripts are there in the app.component.html section