#449

Support Runtime Payloads in Component Events

Opendanshifopened 9d ago

Feature Request: Support Runtime Payloads in Component Events

Summary

Add support for passing runtime payloads when components emit events.

Currently, component events support emitting an event name, for example:

emit("press");

We propose extending this API to support an optional payload:

emit("press", {
  id: link.id,
  url: link.url,
});

This allows components to send interaction data to the action system without writing that data into the shared state store.

Problem

Today, a component that needs to pass runtime data to an action has no direct way to include that data in its event.

For example, consider a reusable Links component. When a user clicks a link, the component knows which link was clicked.

The generated JSON spec should decide what happens next:

  • Open the link in the same tab.
  • Open the link in a new tab.
  • Track the click.
  • Execute multiple actions.

The component should not need to know which actions are available or which actions the agent has selected.

One possible workaround is to write the clicked link data into shared state and then emit the event:

set("/events/linkClick", {
  id: link.id,
  url: link.url,
});

emit("press");

However, this uses application state as an event transport mechanism. It introduces shared state paths, extra state updates, and possible race conditions when multiple events occur close together.

Proposed API

Extend the event emission API with an optional payload.

type Emit = (
  event: string,
  payload?: unknown
) => void;

Example:

emit("press", {
  id: link.id,
  url: link.url,
});

The existing API must continue to work:

emit("press");

The payload should be transient and scoped to the event dispatch. It should not be written to the shared state store automatically.

Proposed JSON API

Allow action parameters to reference the payload of the event that triggered them.

Proposed syntax:

{
  "type": "Links",
  "props": {
    "links": [
      {
        "id": "docs",
        "label": "Documentation",
        "url": "https://example.com/docs"
      }
    ]
  },
  "on": {
    "press": [
      {
        "action": "open_link",
        "params": {
          "url": {
            "$event": "url"
          },
          "target": "_blank"
        }
      },
      {
        "action": "track_click",
        "params": {
          "linkId": {
            "$event": "id"
          }
        }
      }
    ]
  }
}

Note: $event is a proposed syntax, not an existing feature. The issue should consider whether this is the best syntax and whether the entire payload should also be accessible.

Expected Behavior

  1. A component emits an event with an optional payload.
  2. The event dispatcher receives both the event name and payload.
  3. The dispatcher finds the action bindings for that event.
  4. The action parameter resolver can access the event payload.
  5. All actions bound to the same event can access the same payload.
  6. Each event dispatch has its own payload.
  7. Existing events without payloads continue to work.

Design Requirements

  • Keep components generic and independent of application action names.
  • Let the JSON spec decide which actions run.
  • Support multiple action bindings for one event.
  • Support static action parameters alongside dynamic event payload values.
  • Do not require components to write temporary interaction data into shared state.
  • Keep event payloads scoped to their dispatch.
  • Keep the feature framework-agnostic so all supported renderers can use it.
  • Preserve existing event behavior and API compatibility.

Important Considerations

Backward compatibility

The payload must be optional. Existing calls to emit("press") must remain valid.

Event isolation

Two events emitted close together must not share or overwrite each other's payload.

Action execution

All action bindings for one event should receive the correct payload. The implementation should define whether multiple actions execute sequentially or concurrently, consistent with the existing dispatcher behavior.

Payload typing

Consider whether the public API should use unknown or a JSON-compatible type such as Record<string, unknown>. The choice should match the library's runtime and serialization constraints.

Validation

Event payloads should be treated as runtime input. Action parameter validation must still apply.

EventHandle API

Review whether the event payload should also be supported by EventHandle.emit() and other event-related APIs, not only the component's emit() function.

Acceptance Criteria

  • emit(event, payload) is supported.
  • emit(event) continues to work.
  • Event payloads are available when resolving action parameters.
  • JSON action bindings can reference event payload fields.
  • Multiple actions bound to one event can access the same payload.
  • Separate event dispatches cannot accidentally share payload data.
  • No shared state update is required to transport event data.
  • Existing event behavior and preventDefault behavior remain unchanged.
  • Tests cover payload resolution, multiple actions, missing payloads, and consecutive events.
  • Documentation includes a component example and a JSON spec example.

Expected Benefit

This feature provides a clean separation of responsibilities:

  • Component: Reports what happened and provides interaction data.
  • JSON spec: Decides which actions to execute and how to use the data.
  • Action handlers: Execute application behavior.

This makes components more reusable and keeps runtime interaction data separate from persistent application state.

Scope

This issue proposes event payload support. It does not require a new action execution API or changes to the action registry.

The implementation should extend the existing event and action-binding architecture where possible.

Activity

0

No activity yet.