Skip to main content
Content type plugins let you add new question types to Feedal. Your UI runs in a sandboxed iframe; the host page communicates with it via a postMessage protocol. The @feedal/content-type-sdk package provides a minimal bridge so you don’t have to implement the protocol yourself.

Security model

Community plugin UI never runs in the Feedal origin. Your bundle is loaded in an iframe with sandbox="allow-scripts" only:
  • No access to the parent page’s DOM, localStorage, or cookies
  • No cross-origin network requests from the iframe (except to your own CDN for the bundle itself)
  • postMessage is the only communication channel between your plugin and Feedal
This means respondent data stays in Feedal’s hands — your iframe receives the current answer value and node config, and can only send back an answer value or a size hint.

How it works


Installing the SDK


SDK reference

onRender(callback)

Registers a callback that fires every time Feedal sends a PLUGIN_RENDER message. Returns an unsubscribe function. The RenderPayload passed to the callback:

sendAnswer(value)

Sends the respondent’s answer to the host page. Call this when the user makes a selection or submits input. value can be any JSON-serialisable type.

signalReady()

Signals to the host page that your iframe has finished loading and is ready to receive PLUGIN_RENDER messages. Call this once, after your bundle is initialised.

sendSize(height)

Requests the host page to resize the iframe to height pixels. Call this whenever your content changes height to avoid scroll bars or clipped content.

Raw postMessage protocol

If you’re not using the SDK, here are the raw message types: Host → iframe iframe → host

Building your bundle

Your renderer must be a self-contained ES module bundle served over HTTPS. The renderer_bundle_url in your manifest must point to this file.
The bundle is loaded into the iframe as a <script type="module">. There is no HTML shell — your bundle is responsible for creating DOM elements and appending them to document.body.

Minimal example


Validation

What the framework handles

Feedal enforces one rule on behalf of every question type, including plugins: required fields. If node.data.is_required is true and the respondent hasn’t submitted an answer yet, the runner blocks navigation and shows “This field is required.” before your plugin ever receives a PLUGIN_RENDER event for the next node. That’s the only validation the framework performs for plugins. Everything else is your responsibility.

What your plugin handles

All content-level validation — format checks, range limits, minimum selections, custom business rules — must be enforced inside the iframe. The pattern is:
  1. Show the input and let the respondent interact.
  2. When input is invalid, display an inline error message inside the iframe and do not call sendAnswer().
  3. Only call sendAnswer(value) once the value is valid.
Because the runner only advances when it receives a PLUGIN_ANSWER message, withholding sendAnswer() is the complete and correct way to block navigation. There is no separate validation API.

The needsContinueButton manifest field

By default, plugins behave like choice or rating questions: the runner expects sendAnswer() to be called on a single user action and does not render an explicit Continue button. If your plugin has a multi-step input or needs the respondent to explicitly confirm before advancing (for example, a signature pad or a file picker with a preview step), set needs_continue_button: true in your manifest:
When needs_continue_button is true, Feedal renders a themed Continue button below your iframe — the same button used for text input questions. The respondent clicks it to advance; your plugin should already have called sendAnswer() with the current value before that click. If needs_continue_button is false (the default), no Continue button is rendered. Your plugin should call sendAnswer() as the final confirmation action.

answerSchema

The answerSchema field in the manifest is informational only. It documents the shape of the value you pass to sendAnswer() and is used for type hints in the developer portal and the Results Hub. Feedal does not validate answer values against this schema at runtime.

Registering with the manifest

Add a content_type extension point to your app manifest:
Once your app is approved in the marketplace, the question_type_id becomes available in the studio’s Add content panel and in the form graph as "question_type": "plugin:signature-capture".

Testing locally

During development, simulate the postMessage protocol in a plain HTML file to test your bundle without a live Feedal account:
Point the iframe src to a local server that serves your bundle.

Next steps

Developer portal

Submit your plugin for marketplace review.

Marketplace

Browse community and official plugins.