Get your free splitforms access key
Sign up at splitforms.com, verify your email, and copy your access key from the dashboard. No credit card required.
Contact form · Angular
Reactive Forms give you typed, synchronous validation; HttpClient gives you a typed Observable POST. Wire them to the splitforms endpoint and ship a contact form with no service, no controller, and no backend. Standalone component, Angular 17+ — works in NgModule apps too.

No server, API route, or SDK. Your Angular form posts straight to one endpoint.
Every submission is emailed to you and saved to a searchable dashboard — spam filtered before it reaches you.
It's your own Angular markup and styles. Splitforms is only the backend, so nothing constrains how the form looks.
Copy-paste ready
Replace YOUR_ACCESS_KEY with the key from your dashboard — that's the whole integration. No SDK to install, no build step, just the typescript you already write.
// contact-form.component.ts — Angular 17+ standalone component
import { Component, inject, signal } from "@angular/core";
import { CommonModule } from "@angular/common";
import { ReactiveFormsModule, FormBuilder, Validators } from "@angular/forms";
import { HttpClient } from "@angular/common/http";
@Component({
selector: "app-contact-form",
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<input type="hidden" formControlName="access_key" />
<label>
Name
<input type="text" formControlName="name" />
<small *ngIf="form.controls.name.touched && form.controls.name.invalid">
Name is required.
</small>
</label>
<label>
Email
<input type="email" formControlName="email" />
<small *ngIf="form.controls.email.touched && form.controls.email.invalid">
A valid email is required.
</small>
</label>
<label>
Message
<textarea formControlName="message"></textarea>
<small *ngIf="form.controls.message.touched && form.controls.message.invalid">
Please add a message.
</small>
</label>
<!-- Honeypot — invisible to humans -->
<input type="checkbox" formControlName="botcheck" hidden tabindex="-1" />
<button type="submit" [disabled]="status() === 'loading'">
{{ status() === 'loading' ? 'Sending…' : 'Send' }}
</button>
<p *ngIf="status() === 'ok'">Thanks — we'll be in touch.</p>
<p *ngIf="status() === 'err'">Something went wrong. Try again?</p>
</form>
`,
})
export class ContactFormComponent {
private fb = inject(FormBuilder);
private http = inject(HttpClient);
status = signal<"idle" | "loading" | "ok" | "err">("idle");
form = this.fb.group({
access_key: ["YOUR_ACCESS_KEY"],
name: ["", Validators.required],
email: ["", [Validators.required, Validators.email]],
message: ["", Validators.required],
botcheck: [false],
});
submit() {
if (this.form.invalid) {
this.form.markAllAsTouched();
return;
}
this.status.set("loading");
// Build a real multipart FormData so file fields (if you add them) work too.
const fd = new FormData();
Object.entries(this.form.getRawValue()).forEach(([k, v]) =>
fd.append(k, String(v))
);
this.http
.post<{ success: boolean; message?: string }>(
"https://splitforms.com/api/submit",
fd
)
.subscribe({
next: (data) => {
this.status.set(data.success ? "ok" : "err");
if (data.success) this.form.reset({ access_key: "YOUR_ACCESS_KEY" });
},
error: () => this.status.set("err"),
});
}
}How to add it
To add a contact form to a Angular website you need three things: a free splitforms access key, the typescript snippet above, and your key pasted into it. No backend, server, or SDK — the form posts to one URL and every submission lands in your inbox and dashboard.
Sign up at splitforms.com, verify your email, and copy your access key from the dashboard. No credit card required.
Copy the Angular code example into your project and replace YOUR_ACCESS_KEY with the key from step 1.
Submissions arrive in the splitforms dashboard within seconds. Free includes inbox delivery; Pro adds Slack, Discord, Sheets, or any signed webhook URL.
Where submissions go
Every submission is emailed to you and saved to a searchable dashboard — spam filtered before it ever reaches you. Search, export to CSV, or forward it to a webhook or Slack on Pro.

No backend needed
Your Angular form posts standard FormData to one URL. Splitforms validates the access key, runs the spam classifier, and forwards it to your email — so there's no server, API route, or database for you to build or maintain.

Best practices
The difference between a form that works in the demo and one that survives launch traffic — the production-tested defaults, in priority order.

How SplitForms works
Connect your form, collect every submission, and send data where it needs to go — without building backend infrastructure.

Point your form to your unique SplitForms endpoint. That's it.

We instantly capture and organize every submission in your inbox.

Send data to email, spreadsheets, CRMs, webhooks, and 7,000+ apps.

No credit card required. Set up in under 60 seconds.
Connect & automate
SplitForms works with the destinations you route to and the platforms you build on — from Slack and Sheets to WordPress, Shopify, and Next.js.

Trusted by indie teams and agencies shipping forms worldwide
Testimonials
40 quotes on record — from indie hacks to agency migrations.
“I replaced a Lambda + DynamoDB + SES contact form with six lines of HTML. It took eleven minutes, and the dashboard is better than what I was going to build.”
“We migrated 14 client sites off Formspree in a single weekend. The price is a third of what we paid, the API is more honest, and the spam filter actually works.”
“The webhook payload is signed, idempotent, and well-shaped. It reads like code from a competent team, not a CRUD app held together with duct tape.”
“I stopped reaching for Typeform on small marketing sites. splitforms covers 90% of the use case at none of the bloat.”
“I onboarded our whole agency in an afternoon. The MCP integration meant Cursor literally dropped the form straight into our client repos for us.”
“The free plan gave me 500 submissions before I paid a cent, and Pro is five dollars a month. I've spent more on coffee deciding which backend to use.”
“Spam went from forty junk entries a day to zero, with no reCAPTCHA puzzle ruining the form. The honeypot and time-trap just quietly do their job.”
“Point the form action at one endpoint and you're done. No SDK, no client library, no build step. This is how a form backend should feel.”
“Leads land in Slack the second someone submits, and a copy goes to Google Sheets for the sales team. I wired both up in under ten minutes.”
“I run a static Hugo site on a five-dollar VPS. splitforms gave it a real contact form without me standing up a single server.”
Questions
Build a FormGroup with FormBuilder, then on (ngSubmit) append each control's value (plus access_key) to a FormData and call httpClient.post('https://splitforms.com/api/submit', fd).subscribe(…). Inspect data.success in next; handle 4xx in error.
Reactive Forms (FormBuilder, formControlName). They're synchronous, fully typed, unit-testable, and scale to cross-field validators. Template-driven forms with ngModel work for tiny forms but get unwieldy and harder to test. splitforms is form-agnostic — it only cares about the multipart body.
HttpClient routes any non-2xx response to the error callback of .subscribe({ next, error }) (or to catchError in the pipe). Inspect err.error?.message for splitforms' user-facing message. 2xx responses with success: false (e.g. spam flagged) still arrive in next — always check data.success there too.
Yes. The component above is standalone (standalone: true, imports ReactiveFormsModule). No NgModule changes. If you're on a NgModule-based app, just declare the component and provide HttpClient once via provideHttpClient() (or import HttpClientModule) in your app config.
You can't fully — Angular compiles to client-side JavaScript, so anything the component reads ends up in the bundle. The right mitigation is domain-locking the key in the splitforms dashboard (Settings → Allowed domains). For true server-side secrecy, proxy through an Angular Universal server route that injects the key from process.env server-side.
Yes, but POST from the browser, not during server rendering. Guard the HttpClient call with isPlatformBrowser(platformId) or keep it inside the (ngSubmit) handler (which only fires client-side). The form markup can render on the server; the submission must originate from the browser.
Angular's HttpClient inspects the body argument: pass a FormData instance and it sets Content-Type: multipart/form-data; boundary=… automatically. If you manually add a Content-Type: multipart/form-data header in the options (a common copy-paste from older AngularJS tutorials), Angular doesn't know the boundary string and the request body can't be parsed — splitforms returns 400 malformed body. Same rule as fetch and jQuery: leave the header alone when the body is FormData.
Unlike fetch, Angular's HttpClient treats any non-2xx status as an error and routes it to the error callback of .subscribe({ next, error }) (or to catchError in the pipe). That means splitforms' 4xx responses (invalid key, spam flagged, rate-limit) hit error, NOT next — so data.success is never checked for those cases and you'll miss the user-facing message entirely. Read err.error?.message in the error handler, or normalize both paths with catchError. Remember: 2xx with success: false still lands in next, so check data.success there too.
Bootstrap-style 'show errors after submit' isn't automatic in Angular — a control's errors are present in form.controls.email.invalid but the *ngIf template guard usually checks .touched too, so nothing displays until the user blurs the field. In submit(), call this.form.markAllAsTouched() before returning early on this.form.invalid, or users click Send on an empty form and see nothing happen. Also prefer the strict this.fb.group over fb.group (pre-14) which returned FormGroup<any> and let typos like form.controls.emial compile.
Simple pricing
Choose a plan that fits your workflow — from a free form endpoint to full automations, exports, and higher submission limits.
Free forever
For side projects and indie devs.
For agencies and growing products.
Pay $59. 3 years sorted.
No credit card required on Free • Cancel anytime