How to integrate Netlify Forms in an Angular app
I will walk you through how to create a form in Angular, and how to connect it to Netlify's Form functionality. We’ll create a contact form in our Angular component and a hidden form in index.html for Netlify's bot to reckognize it. It's also important to add a hidden input field in both forms to help Netlify identify the form submission.
Updated: Jun 20, 2024
Published: Jul 23, 2023
Netlify Forms
Netlify has a feature where you can send form submissions directly to them through your web application. Setting it up is explained in detail on their official web site, and I recommend reading it for more details. In this blog post, we'll go through the specifics of setting it up in Angular 16 (the principles are the same for any version of Angular 2 and above.)
First, we need to enable Form submissions through our app's dashboard on Netlify.
When that's done, we're ready to receive form submissions, but we have to create our form first. We'll be creating a contact form where our visitors can enter their name, email address, and a message.
Start with the form in index.html
Since Angular is a single page application, our main form will be created at runtime. Netlify is not able to detect our form made with Angular, and we therefore need to create a form on the page we’re always loading, which is index.html
. Most Angular apps hosted on Netlify will redirect all traffic to index.html
, let Angular bootstrap, and then the app will take care of the routing. Netlify only sees our index.html
file (before Angular bootstraps,) and that’s why we include our hidden form there. Don’t worry about duplicating a lot of code, we only add the inputs with the same names as we use for our Angular form.
index.html
<body>
<app-root></app-root>
<!-- Hidden form used for Netlify Forms to pick it up -->
<form name="contact" method="POST" netlify hidden>
<input type="hidden" name="form-name" value="contact" />
<input type="hidden" id="name" name="name" />
<input type="hidden" id="email" name="email" />
<textarea type="hidden" id="body" name="body"></textarea>
</form>
</body>
We need to add type="hidden"
to prevent displaying the form, because we only want Netlify’s bot to recognize it, not our visitors. Remeber to also add the netlify
attribute, and an input filed which is used by Netlify to determine which form we are submitting:
<input type="hidden" name="form-name" value="contact" />
This field is not something the user will see nor interact with, but it’s important to include it when we submit our form. If we don’t include it, Netlify won’t be able to receive the form submission.
Let’s add our contact form to Angular
Next up is creating our actual form the user will see and interact with. You can add it to any page you already have, or create a new page. We’ll be using a new component, ContactComponent
, and create a reactive Angular form with some simple, but important, validation. I have omitted styling (css) from this tutorial, but we'll end up with something similar to this form, which is the same form I have on my home page, https://reitgames.com/contact. The form on my page is made exactly the same as we’ll do in this blog post.
I personally like to send the form manually, with code, because I’m able to redirect the user to a different page after the form is sent, or conditionally display a message, snackbar, modal, or any other kind of essential feedback to the user. In this tutorial, we’ll hide the form, and display a "thank you message," after the form is submitted.
contact.component.html
<div>
<div *ngIf="isContactFormSubmitted">
Thank you for reaching out! Your message has been successfully sent. I'll get back to you as soon as I can.
</div>
<form
*ngIf="!isContactFormSubmitted"
name="contact"
[formGroup]="contactForm"
method="POST"
(ngSubmit)="onSubmit($event)"
netlify>
<!-- This input field is very important to add, at least when we submit the form, making sure Netlify can receive the submission -->
<input type="hidden" name="form-name" value="contact" />
<label for="name">Name</label>
<div *ngIf="name && name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
<small *ngIf="name.errors?.['required']">Name is required</small>
</div>
<input id="name" type="text" formControlName="name" required />
<label for="email">Email</label>
<div
*ngIf="email && email.invalid && (email.dirty || email.touched)"
class="alert alert-danger">
<small *ngIf="email.errors?.['required']">Email is required</small>
<small *ngIf="email.errors?.['email']">Enter a valid email address</small>
</div>
<input id="email" type="email" formControlName="email" required />
<label for="body">Message</label>
<div *ngIf="body && body.invalid && (body.dirty || body.touched)" class="alert alert-danger">
<small *ngIf="body.errors?.['required']">Message is required</small>
</div>
<textarea id="body" type="text" rows="4" cols="50" formControlName="body" required></textarea>
<button type="submit" [disabled]="!contactForm.valid">Send message</button>
</form>
</div><div *ngIf="isContactFormSubmitted">
Thank you for reaching out! Your message has been successfully sent. I'll get back to you as soon as I can.
</div>
<form
*ngIf="!isContactFormSubmitted"
name="contact"
[formGroup]="contactForm"
method="POST"
(ngSubmit)="onSubmit($event)"
netlify>
<!-- This input field is very important to add, at least when we submit the form, making sure Netlify can receive the submission -->
<input type="hidden" name="form-name" value="contact" />
<label for="name">Name</label>
<div *ngIf="name && name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
<small *ngIf="name.errors?.['required']">Name is required</small>
</div>
<input id="name" type="text" formControlName="name" required />
<label for="email">Email</label>
<div
*ngIf="email && email.invalid && (email.dirty || email.touched)"
class="alert alert-danger">
<small *ngIf="email.errors?.['required']">Email is required</small>
<small *ngIf="email.errors?.['email']">Enter a valid email address</small>
</div>
<input id="email" type="email" formControlName="email" required />
<label for="body">Message</label>
<div *ngIf="body && body.invalid && (body.dirty || body.touched)" class="alert alert-danger">
<small *ngIf="body.errors?.['required']">Message is required</small>
</div>
<textarea id="body" type="text" rows="4" cols="50" formControlName="body" required></textarea>
<button type="submit" [disabled]="!contactForm.valid">Send message</button>
</form>
</div>
Let's start describing the first part of the form, the <form>
-element.
We add (ngSubmit)="onSubmit($event)"
on the form element because we want to manually send the request (with code) to Netlify, and alter the contend displayed on our page after the submission went through. We add the netlify
attribute, and make sure method="POST"
. We then connect the formGroup, contactForm, that we'll create in our component.
Inside the form, we have the same inputs as we added to our index.html file previously. We also add some labels, and some conditions inside the form itself to display certain warnings to the user depending on the requirement and validation of the form fields. Note that we only display the warnings to the user after they start inputing text. We do this by checking the following: name.invalid && (name.dirty || name.touched)
. This is important because we don't want to unintentionally bother the user with warnings before they even started writing anything.
The next step is to connect the HTML to our component code. We use Angular's reactive forms, to create the form itself.
contact.component.ts
export class ContactComponent implements OnInit {
contactForm: FormGroup;
isContactFormSubmitted = false;
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.contactForm = new FormGroup({
name: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
body: new FormControl('', [Validators.required]),
});
}
onSubmit(evt: SubmitEvent) {
evt.preventDefault();
const formData = this.contactForm.value;
// This is important. We need to add the hidden field to make sure Netlify picks up the form submission.
formData['form-name'] = 'contact';
const headers = new HttpHeaders({
Accept: 'text/html',
'Content-Type': 'application/x-www-form-urlencoded',
});
this.http
.post('/', new URLSearchParams(formData).toString(), { headers, responseType: 'text' })
.subscribe(() => {
this.isContactFormSubmitted = true;
});
}
// functions used in the html template
get name() {
return this.contactForm.get('name');
}
get email() {
return this.contactForm.get('email');
}
get body() {
return this.contactForm.get('body');
}
}
The contact form contains some validators, e.g. [Validators.required], which are useful
In the Angular component, we use the httpClient
to send a post request. We send the post request to any path on our site, and Netlify will pick it up (NOTE: This does not work in local development on localhost.) It’s important to add a header, 'Content-Type':'application/x-www-form-urlencoded'
, for Netlify to process the data we send correctly. We also need to add an optional parameter, responseType:'text'
, because the response from Netlify will be HTML, not JSON (which is the default responseType for Angular’s httpClient.) If we forget to change the responseType, we'll get errors in the console when we receive the response from Netlify. The response we get from Netlify looks like the image I've attached below.
We are only interested to know if the form was submitted correctly, not the response itself. If the form submission was sent correctly, we set our variable, isContactFormSubmitted
to true
, hide the form, and render our "Thank you message" to the user.
With this, we're equipped to send form submissions in our Angular app through Netlify without setting up external dependencies. As of this writing, we get 100 submissions for free each month, which I sufficient for most. We can always upgrade the plan if we want to accept more than 100 submissions each month.
I decided to create this blog post because Netlify already has a tutorial for React and Vue, but not Angular! If someone from Netlify is reading this, and liked the blog post, please reach out (through my contact form,) and we can complete the trio on how to integrate forms with Netlify by adding a revised version of this blog post on Netlify's official Blog.
If you liked this blog post, or want me to elaborate on other topics, consider following me on Twitter, @ReitGames, and mention me in a Tweet. Happy coding!