NBForms

Framework Snippets

Ready-to-use code for every major frontend framework and language.

The Setup tab generates a complete, copy-pasteable code snippet for your chosen framework. Switch tabs to get the code tailored to your stack — it updates live as you add, remove, or reorder fields.

Supported frameworks

HTML · React · Next.js · Remix · Gatsby · Vue · Nuxt · Astro · Svelte · SvelteKit · Angular · jQuery · Alpine.js · HTMX · PHP · Laravel


HTML

The simplest integration. Just a <form> tag pointing at your endpoint.

<form action="https://api.nbforms.com" method="POST">
  <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
  <label>Name</label>
  <input type="text" name="name" required />
  <label>Email</label>
  <input type="email" name="email" required />
  <label>Message</label>
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Works in any static site, WordPress, Webflow, Framer, or anything that renders HTML.


React / Next.js / Remix / Gatsby

These snippets intercept the submit event with fetch so the page does not reload. They manage a status state (idle | sending | success | error) and disable the submit button while sending.

// React example
import { useState } from 'react'

export default function ContactForm() {
  const [status, setStatus] = useState('idle')

  async function handleSubmit(e) {
    e.preventDefault()
    setStatus('sending')
    const res = await fetch('https://api.nbforms.com', {
      method: 'POST',
      body: new FormData(e.target),
    })
    setStatus(res.ok ? 'success' : 'error')
  }

  if (status === 'success') return <p>Thanks! We'll be in touch soon.</p>

  return (
    <form onSubmit={handleSubmit} data-nbf="YOUR_FORM_ID">
      <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
      {/* your fields */}
      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending…' : 'Send'}
      </button>
    </form>
  )
}

Next.js adds 'use client' at the top. Remix uses <Form> from @remix-run/react and a server-side action function.


Vue / Nuxt

<template>
  <p v-if="success">Thanks! We'll be in touch soon.</p>
  <form v-else @submit.prevent="submit" data-nbf="YOUR_FORM_ID">
    <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
    <!-- your fields -->
    <button type="submit" :disabled="sending">
      {{ sending ? 'Sending…' : 'Send' }}
    </button>
  </form>
</template>

<script setup>
const sending = ref(false)
const success = ref(false)

async function submit(e) {
  sending.value = true
  const res = await fetch('https://api.nbforms.com', {
    method: 'POST',
    body: new FormData(e.target),
  })
  success.value = res.ok
  sending.value = false
}
</script>

Astro

Astro forms use a vanilla JS event listener in a <script> block. The form can use a standard action attribute as a fallback.

---
---
<form id="nbform" action="https://api.nbforms.com" method="POST" data-nbf="YOUR_FORM_ID">
  <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
  <!-- your fields -->
  <button type="submit">Send</button>
  <p id="success" hidden>Thanks! We'll be in touch soon.</p>
</form>

<script>
  const form = document.getElementById('nbform')
  form.addEventListener('submit', async (e) => {
    e.preventDefault()
    const btn = form.querySelector('button[type=submit]')
    btn.disabled = true
    btn.textContent = 'Sending…'
    const res = await fetch(form.action, { method: 'POST', body: new FormData(form) })
    if (res.ok) document.getElementById('success').hidden = false
    btn.disabled = false
    btn.textContent = 'Send'
  })
</script>

PHP

The PHP snippet submits from the server side using file_get_contents with a stream context, then re-renders the page with a success state.


Alpine.js

Alpine.js handles the state inline using x-data:

<div x-data="{ sending: false, success: false }">
  <p x-show="success">Thanks! We'll be in touch soon.</p>
  <form x-show="!success" data-nbf="YOUR_FORM_ID"
    @submit.prevent="
      sending = true;
      fetch('https://api.nbforms.com', { method: 'POST', body: new FormData($el) })
        .then(r => { success = r.ok; sending = false })
    ">
    <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
    <!-- your fields -->
    <button type="submit" :disabled="sending"
            x-text="sending ? 'Sending…' : 'Send'"></button>
  </form>
</div>

HTMX

HTMX uses hx-post to submit the form and swap the response HTML into the target element.

<div id="contact-wrapper">
  <form hx-post="https://api.nbforms.com"
        hx-target="#contact-wrapper"
        hx-swap="innerHTML"
        data-nbf="YOUR_FORM_ID">
    <input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
    <!-- your fields -->
    <button type="submit">Send</button>
  </form>
</div>

The NBForms endpoint returns { "success": true } — configure your HTMX response handling accordingly, or use the redirect setting to send users to a thank-you page.


Notes

  • All snippets include a data-nbf attribute on <form> scoped to the form ID. This is used by the optional pixel tracker and for CSS scoping.
  • The _token hidden input is mandatory in every snippet — remove it and submissions will fail.
  • The Setup tab generates the CSS style block for the chosen theme automatically. Copy Full HTML to get the complete standalone file including styles.

On this page