Skip to content

WebhookForm doesn't work in SSG published sites #11

@vianmora

Description

@vianmora

Problem

When a site is published via webstudio-publisher (which uses --template ssg), the Webhook Form component does not work. Submitting the form either:

  • Does a plain HTML GET request to /action?field=value&... → 404
  • Or does nothing if the action resource is not configured

Related to #1 — both issues stem from the same root cause: the SSG template has no server-side handler for form submissions.

Root cause

The Webhook Form is designed for Remix/React Router environments. It works in two layers:

  1. Remix/RR version (sdk-components-react-remix / sdk-components-react-router): uses useFetcher to POST to the current page's Remix action handler, which looks up the resource config and fetches the external webhook URL server-side.

  2. Plain SSG version (sdk-components-react): just renders a <form> with no submit handler and no data-state attribute — nothing intercepts the submission.

The SSG template (packages/cli/templates/ssg/) has no action handler (it's a static Vike site). Additionally, the action prop on the form contains the resource name (e.g. "action"), not the URL — the actual URL lives in getResources({ system }).action which is only accessible server-side.

Proposed fix (in webstudio-fork)

Five files to change:

  1. New packages/sdk-components-react/src/action-resources-context.ts — React context to pass the action resource map to the component tree
  2. packages/sdk-components-react/src/webhook-form.tsx — add data-state={state} and an onSubmit handler that reads the resource URL from context and does a client-side fetch, then calls onStateChange
  3. packages/cli/templates/ssg/app/route-templates/html/+data.ts — include Object.fromEntries(getResources({ system }).action) in the returned data
  4. packages/cli/templates/ssg/app/route-templates/html/+Page.tsx — wrap with <ActionResourcesContext.Provider value={data.actionResources}>
  5. Export the new context from the package index

This allows the SSG WebhookForm to:

  • Read the configured webhook URL + method + headers from context
  • POST the form data to that URL directly from the browser
  • Update the form's success/error state via onStateChange → React re-render

Workaround (until fix is merged)

Add an HTML Embed component on the page with this script:

<script>
document.addEventListener('submit', function(e) {
  const form = e.target;
  if (!form || form.tagName !== 'FORM') return;
  e.preventDefault();

  const data = Object.fromEntries(new FormData(form));
  delete data['ws--form-id'];
  delete data['ws--form-bot'];

  const notifyState = (state) => {
    const key = Object.keys(form).find(k => k.startsWith('__reactProps'));
    if (key && form[key]?.onStateChange) {
      form[key].onStateChange(state);
    }
  };

  fetch('https://YOUR-WEBHOOK-URL', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  })
  .then(r => notifyState(r.ok ? 'success' : 'error'))
  .catch(() => notifyState('error'));
});
</script>

Leave the form's Action field empty. This intercepts the submit, POSTs to your webhook (n8n, Resend, etc.) and correctly triggers the success/error state via React internals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions