EdgeInteract
Custom Interaction Renderer
Custom interaction types require custom renderer components. Define your own interaction type key, implement a React component that receives the payload and calls respond(), then register it with registerInteractionRenderer.
Step 1 — Define Your Interaction Type Key
Custom type keys must be namespaced to avoid collision with built-in types. Use a reverse-domain prefix:
// Your custom type key
export const SPECIAL_REVIEW_TYPE = 'com.acme/special-review';
Step 2 — Define Payload and Response Types
// TypeScript types for your custom interaction
export interface SpecialReviewPayload {
entityId: string;
entityType: string;
reviewCriteria: string[];
attachmentUrls: string[];
}
export interface SpecialReviewResponseData {
decision: 'approved-with-changes' | 'approved' | 'declined';
requiredChanges?: string[];
reviewNotes: string;
}
Step 3 — Implement the Renderer Component
import { InteractionComponentProps } from 'edge-interact-core';
import { useState } from 'react';
export function SpecialReviewComponent({
interaction,
respond
}: InteractionComponentProps<SpecialReviewPayload>) {
const { payload } = interaction;
const [decision, setDecision] = useState<string>('');
const [notes, setNotes] = useState('');
const [changes, setChanges] = useState<string[]>([]);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => {
if (!decision) return;
setIsSubmitting(true);
try {
await respond(decision, {
decision,
requiredChanges: decision === 'approved-with-changes' ? changes : undefined,
reviewNotes: notes
} as SpecialReviewResponseData);
} finally {
setIsSubmitting(false);
}
};
return (
<div className="special-review-component">
<h3>{interaction.title}</h3>
<p>Entity: {payload.entityType} — {payload.entityId}</p>
<ul>
{payload.reviewCriteria.map(c => <li key={c}>{c}</li>)}
</ul>
{/* ... render attachments, decision selector, notes input ... */}
<div>
<button onClick={() => setDecision('approved')}>Approve</button>
<button onClick={() => setDecision('approved-with-changes')}>Approve with Changes</button>
<button onClick={() => setDecision('declined')}>Decline</button>
</div>
<textarea value={notes} onChange={e => setNotes(e.target.value)}
placeholder="Review notes..." />
<button onClick={handleSubmit} disabled={!decision || isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit Review'}
</button>
</div>
);
}
Step 4 — Register the Renderer
// Register before the app renders (e.g., in main.tsx or App.tsx)
import { registerInteractionRenderer } from 'edge-interact-ui';
import { SpecialReviewComponent } from './interactions/SpecialReviewComponent';
import { SPECIAL_REVIEW_TYPE } from './interactions/types';
// Register at module load time — not inside a component
registerInteractionRenderer(SPECIAL_REVIEW_TYPE, SpecialReviewComponent);
Step 5 — Register a Server-Side Validator
// Register custom response validator on the server
builder.Services.AddInteractionTypeValidator<SpecialReviewValidator>(SPECIAL_REVIEW_TYPE);
Custom Type Checklist
| Item | Status |
|---|---|
| Namespaced type key (reverse-domain prefix) | Required |
| TypeScript payload interface | Required |
| TypeScript response data interface | Required |
React renderer component (receives interaction + respond) | Required |
registerInteractionRenderer(typeKey, Component) | Required (client) |
| Server-side response validator | Strongly recommended |