Skip to content

Qwik

routeAction$

routeAction$ runs on the server, so the submission works before the page has hydrated and keeps working with JavaScript disabled.

tsx
import { component$ } from "@builder.io/qwik";
import { Form, routeAction$ } from "@builder.io/qwik-city";

const FORMSPARK_ACTION_URL = "https://submit-form.com/your-form-id";

export const useContactAction = routeAction$(async (data) => {
  await fetch(FORMSPARK_ACTION_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify(data),
  });
  return { success: true };
});

export default component$(() => {
  const action = useContactAction();

  return (
    <div>
      <Form action={action}>
        <label>
          <span>Message</span>
          <textarea name="message" />
        </label>
        <button type="submit" disabled={action.isRunning}>
          Send
        </button>
      </Form>

      {action.value?.success && <p>Thanks! We received your message.</p>}
    </div>
  );
});

Fetch

If you would rather submit from the browser, use a $ handler and a signal. The action URL ends up in the client bundle here, which is no different from a plain HTML form: a form id identifies a form, it does not authorize anything, so it is safe to ship to the browser.

tsx
import { component$, useSignal, $ } from "@builder.io/qwik";

const FORMSPARK_ACTION_URL = "https://submit-form.com/your-form-id";

export default component$(() => {
  const message = useSignal("");
  const submitting = useSignal(false);

  const onSubmit = $(async () => {
    submitting.value = true;
    try {
      await fetch(FORMSPARK_ACTION_URL, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: JSON.stringify({ message: message.value }),
      });
      message.value = "";
    } finally {
      submitting.value = false;
    }
  });

  return (
    <form preventdefault:submit onSubmit$={onSubmit}>
      <textarea bind:value={message} />
      <button type="submit" disabled={submitting.value}>
        Send
      </button>
    </form>
  );
});