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:
-
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.
-
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:
- New
packages/sdk-components-react/src/action-resources-context.ts — React context to pass the action resource map to the component tree
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
packages/cli/templates/ssg/app/route-templates/html/+data.ts — include Object.fromEntries(getResources({ system }).action) in the returned data
packages/cli/templates/ssg/app/route-templates/html/+Page.tsx — wrap with <ActionResourcesContext.Provider value={data.actionResources}>
- 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.
Problem
When a site is published via
webstudio-publisher(which uses--template ssg), the Webhook Form component does not work. Submitting the form either:/action?field=value&...→ 404actionresource is not configuredRelated 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:
Remix/RR version (
sdk-components-react-remix/sdk-components-react-router): usesuseFetcherto POST to the current page's Remix action handler, which looks up the resource config and fetches the external webhook URL server-side.Plain SSG version (
sdk-components-react): just renders a<form>with no submit handler and nodata-stateattribute — nothing intercepts the submission.The SSG template (
packages/cli/templates/ssg/) has noactionhandler (it's a static Vike site). Additionally, theactionprop on the form contains the resource name (e.g."action"), not the URL — the actual URL lives ingetResources({ system }).actionwhich is only accessible server-side.Proposed fix (in
webstudio-fork)Five files to change:
packages/sdk-components-react/src/action-resources-context.ts— React context to pass the action resource map to the component treepackages/sdk-components-react/src/webhook-form.tsx— adddata-state={state}and anonSubmithandler that reads the resource URL from context and does a client-sidefetch, then callsonStateChangepackages/cli/templates/ssg/app/route-templates/html/+data.ts— includeObject.fromEntries(getResources({ system }).action)in the returned datapackages/cli/templates/ssg/app/route-templates/html/+Page.tsx— wrap with<ActionResourcesContext.Provider value={data.actionResources}>This allows the SSG WebhookForm to:
onStateChange→ React re-renderWorkaround (until fix is merged)
Add an HTML Embed component on the page with this 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.