The job EmailJS does
A static site needs a contact form; a contact form needs to send email; email needs a server you don't have. EmailJS bridges that: your front-end calls their API with a public key, their servers send through your connected mail account. For a portfolio site's contact form, that's the whole job, done.
Where client-side sending stops
Everything in the browser is public: your template IDs, your public key, your rate limits, all inspectable and scriptable by anyone who opens devtools. There's no way to send transactional email safely (a password reset triggered purely client-side is an attacker's toy), no delivery events you can trust, and the free tier caps fast.
The pattern that scales keeps the form client-side and moves the send server-side: the form captures the contact against a public endpoint, and the email fires from the platform, where secrets live and events flow.
We built that public endpoint the paranoid way, and it shows what client-side surfaces demand: it always returns the same success response whether or not the contact exists, so nobody can use your form to enumerate email addresses, and it's idempotent, so double-submits change nothing. That is the level of thinking your contact form needs, and it's exactly what a client-side sending key can never give you.
We ship a browser-safe public client for exactly this pattern, @nitrosend/sdk/browser, so the front-end capture is typed and clean while every send stays server-side. And the loop it feeds is real: one of our customers wired their website contact form into a three-email welcome flow, built by asking Claude, in one instruction. Form captures, flow nurtures, nothing sent from the browser.
The key design enforces the boundary mechanically. Server code holds a secret key. The browser package takes only a public website key, requires an origins allowlist so another site can't lift the key and reuse it, and is hardcoded to refuse a secret key outright if someone tries to initialize it with one. The safe pattern shouldn't depend on developers reading the docs. It should be the only pattern that runs.
Full parameters and responses live in the REST API docs and the API reference.
Go deeper
The server-side send is one call from Node.js or TypeScript, and serverless functions mean "a backend" is ten lines on Vercel or Cloudflare.