Skip to main content

Frontend Contributions

A frontend contribution describes how an integration appears and behaves in the Playrunner web application. It does not execute the integration's workflow action.

Declare and export the surface​

Declare the frontend entrypoint in the package manifest and expose the same subpath through exports:

{
"playrunner": {
"integration": {
"id": "example",
"frontend": "."
}
},
"exports": {
".": {
"types": "./src/frontend/index.tsx",
"import": "./src/frontend/index.tsx",
"require": "./src/frontend/index.tsx",
"default": "./src/frontend/index.tsx"
}
}
}

The entrypoint must default-export an object satisfying the Integration contract from @playrunner/integration-sdk:

import type { Integration } from '@playrunner/integration-sdk';
import { ExampleConfigPanel } from './ExampleConfigPanel';
import { ExampleSettingsModal } from './ExampleSettingsModal';
import { exampleIconUrl } from './icon';

export const exampleIntegration: Integration = {
id: 'example',
name: 'Example',
category: 'Developer Tools',
description: 'Run an Example action',
icon: exampleIconUrl,
nodeType: 'action',
SettingsModal: ExampleSettingsModal,
ConfigPanel: ExampleConfigPanel,
};

export default exampleIntegration;

The exported id must equal playrunner.integration.id. The contract also supports visibility and availability flags, authentication metadata, ordering, package-owned icons, refresh behavior, and optional settings and config components.

Keep brand assets in the package and resolve them from package source, as the existing packages do with new URL('../../assets/example.svg', import.meta.url).

Choose the icon pattern by brand behavior​

Use the provider's visual behavior to choose between the two supported icon patterns:

  • Fixed-color and multicolor logos, such as Jira, Slack, and Playwright, use a package-owned SVG URL as the integration icon. Render these logos as normal images so their brand colors remain unchanged.
  • Monochrome logos that must follow the active theme, such as GitHub and OpenAI, use a package-owned React SVG component with fill="currentColor". Set that component as the integration icon and reuse it in settings and connection UI.

For a theme-adaptive icon, keep the raw asset URL as a separate export when consumers need it. Do not pass the URL as the integration icon and do not apply the SVG through CSS mask-image; both approaches can render the SVG as an opaque rectangle instead of the intended mark.

export function ExampleIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
className={className}
>
<path d="..." />
</svg>
);
}

Then register and reuse the component:

import { ExampleIcon } from './ExampleIcon';

export const exampleIntegration: Integration = {
id: 'example',
name: 'Example',
category: 'Developer Tools',
description: 'Run an Example action',
icon: ExampleIcon,
nodeType: 'action',
SettingsModal: ExampleSettingsModal,
ConfigPanel: ExampleConfigPanel,
};

<IntegrationSettingsModal
icon={<ExampleIcon className="h-5 w-5 text-[var(--foreground)]" />}
{...settingsModalProps}
/>;

Use the SDK host boundary​

Package UI accesses Playrunner-owned services through @playrunner/integration-sdk. IntegrationSdkProvider supplies the host, and package components call useIntegrationHost() to access authentication, integration persistence, cloud credentials, environments, secrets, and shared UI primitives.

Do not import application internals from apps/frontend/src. Keeping the SDK as the boundary lets the package own its UI without taking a dependency on the host app's private modules.

For connection forms, use the SDK's connection input and autofill guard helpers so credential fields follow the shared browser-autofill behavior. Keep provider-specific setup, settings, and node configuration components in the provider package.

All in-app help and setup-guide links must lead to Playrunner's Docusaurus documentation. Never link a settings dialog directly to a vendor's documentation. Create the provider guide under docs/docs/integration-packages when documentation is in scope, and put any external reference links inside that guide. A missing guide does not justify linking the app to vendor documentation instead.

Use the configured VITE_DOCS_URL with the established Playrunner docs fallback, preserving a deployment's base path. Append the provider route and section anchor, for example docs/integration-packages/microsoft-teams#setup; do not replace the configured base path with an origin-relative URL. Verify that the destination and anchor exist in the documentation build.

Keep the canonical connection-dialog callout: BookOpen in the inset icon container, title and supporting copy, then the guide link with ExternalLink. Open the guide with target="_blank" and rel="noopener noreferrer".

How the host composes the frontend​

Before frontend development, typechecking, or building, the frontend's package script runs the shared composition generator. It scans the frontend app's installed direct production dependencies and writes static imports to:

apps/frontend/src/integrations/generated-package-contributions.ts

apps/frontend/src/integrations/registry.ts validates that every default export is an object and that its ID matches the package manifest. It then adds those contributions to the product registry. Duplicate IDs are rejected.

Local file: dependencies resolve through the frontend app's installed node_modules; Vite preserves symlinks for those package imports. There is no provider-specific Vite or TypeScript alias to add.

Every locally edited package that contributes to the rendered feature must use a file: dependency. For example, local GitHub repository selection requires both entries below because the modal belongs to GitHub while the selector belongs to Playwright:

{
"dependencies": {
"@playrunner/github": "file:../../packages/github",
"@playrunner/playwright": "file:../../packages/playwright"
}
}

After installing, verify the selected modules before debugging runtime state:

npm --prefix apps/frontend ls @playrunner/github @playrunner/playwright --depth=0

Both entries should point into ../../packages. If either resolves to an npm tarball, the frontend can show old package behavior even though the workspace source and another consuming app are current.

Adding a frontend package or changing its metadata still requires updating the frontend artifact's selected dependency and rebuilding the frontend. Connecting credentials or editing a workflow node after deployment only configures the already-bundled contribution.