Developing Apps for Cytoscape Web
- Targets Cytoscape Web App API 1.0 (
@cytoscape-web/api-types)
Reference implementations and documentation for Cytoscape Web app development
This repository is for third-party developers who want to build apps for Cytoscape Web.
You do not need to change the host source code. Your app is loaded by the host through Module Federation (Vite). Apps can add:
- panel components in the right-side App Panel
- menu items in the Apps dropdown
- context menu actions for right-click workflows
Both the host application and this examples repository are needed side by side.
The host runs the Cytoscape Web UI at localhost:5500 and loads your app from
your own dev server, so you never rebuild the host to work on your app.
Editing your app does not hot-reload inside the host. Vite's HMR does not cross the federation boundary — that is a separate feature (
dev.remoteHmr), off by default. Your dev server rebuilds the changed module immediately, but you reload the host page to pick it up.
mkdir cytoscape-web-dev && cd cytoscape-web-dev
git clone https://github.com/cytoscape/cytoscape-web.git
git clone https://github.com/cytoscape/cytoscape-web-app-examples.gitcd cytoscape-web-app-examples
npm install
npm run devcd cytoscape-web
npm install
npm run dev:local- Open
http://localhost:5500 - Open Apps -> App Settings
- Enable one of the example apps
- Open the Apps menu or the right-side App Panel
The production instance of Cytoscape Web loads apps from a curated allowlist
(apps.json) maintained by the core team. There are plans for a dynamic loading mechanism and the public App
store in the future, but for now public app registration is manual. If you would like
to publish your app, please
contact the Cytoscape team.
Copy project-template/ and follow the 5 steps:
cp -r project-template my-app && cd my-apppackage.json— changenameandversionvite.config.ts— changenameinfederation()andDEV_SERVER_PORTsrc/TemplateApp.tsx— changeid(must match MF name),name,resources- Host registry — add an entry for your app in
cytoscape-web/src/assets/apps.local.json:{ "id": "myApp", "name": "My App (display name)", "url": "http://localhost:6000/remoteEntry.js", "version": "0.1.0" }idis the unique identifier (must match the federationnameand yourCyApp.id);nameis the human-readable label shown in App Settings.To test the template before copying, add:
{ "id": "template", "name": "App Template", "url": "http://localhost:5555/remoteEntry.js", "version": "0.1.0" } - Verify —
npm run dev, then confirm in browser
See project-template/README.md for details.
Every app exports one CyAppWithLifecycle object:
import { lazy } from 'react'
import { CyAppWithLifecycle } from 'cyweb/ApiTypes'
import packageJson from '../package.json'
const { version } = packageJson
export const MyApp: CyAppWithLifecycle = {
id: 'myApp',
name: 'My App',
description: 'Short description of your app',
version,
apiVersion: '1.0',
// Declarative resource registration — panels and menu items
resources: [
{
slot: 'right-panel',
id: 'MyPanel',
title: 'My Panel',
component: lazy(() => import('./components/MyPanel')),
},
{
slot: 'apps-menu',
id: 'MyMenuItem',
title: 'My Action',
component: lazy(() => import('./components/MyMenuItem')),
closeOnAction: true,
},
],
// Context menus and event listeners — registered in mount()
mount(context) {
context.apis.contextMenu.addContextMenuItem({
label: 'My App: Log Node Info',
targetTypes: ['node'],
handler: (ctx) => {
const result = context.apis.element.getNode(ctx.networkId, ctx.id!)
if (result.success) console.info('Node:', result.data)
},
})
},
unmount() {
/* clean up event listeners only — context menus are auto-cleaned */
},
}| Guide | Topics |
|---|---|
| Getting Started | Scaffold, configure, register, run |
| Architecture Overview | Module Federation, type system, API layers |
| Registration Patterns | Panels, menus, context menus, upsert, batch |
| Lifecycle & Cleanup | mount/unmount, auto-cleanup, re-enable |
| Troubleshooting | Build errors, runtime errors, FAQ |
| Resource | Description |
|---|---|
| App API Reference | Complete reference for all domain APIs, ResourceApi, Event Bus, error codes, and lifecycle |
@cytoscape-web/api-types (README) |
TypeScript types package — install for IDE support |
| CHANGELOG | Version history for the types package |
| Spec | Scope |
|---|---|
| App API Specification | Full 2000-line spec for all 10 domain APIs |
| Resource Registration Specification | Slot model, lifecycle, cleanup, error boundaries |
| Registration Minimal App Example | End-to-end code walkthrough of all registration paths |
All API methods return ApiResult<T>. Always check result.success before reading result.data.
| API | Import | Purpose |
|---|---|---|
| WorkspaceApi | cyweb/WorkspaceApi |
Current network ID, workspace info, switch network |
| ElementApi | cyweb/ElementApi |
Create/delete nodes and edges, graph traversal queries |
| NetworkApi | cyweb/NetworkApi |
Create/delete networks, import CX2 |
| SelectionApi | cyweb/SelectionApi |
Read and mutate the current selection |
| ViewportApi | cyweb/ViewportApi |
Pan, zoom, fit, read/write node positions |
| TableApi | cyweb/TableApi |
Read and write node/edge attribute tables |
| VisualStyleApi | cyweb/VisualStyleApi |
Set defaults, bypasses, and mappings |
| LayoutApi | cyweb/LayoutApi |
Run layout algorithms |
| ExportApi | cyweb/ExportApi |
Export network as CX2 |
| EventBus | cyweb/EventBus |
Subscribe to host events (useCyWebEvent) |
| AppIdContext | cyweb/AppIdContext |
Per-app context (useAppContext) for resource and context menu APIs |
| ApiTypes | cyweb/ApiTypes |
TypeScript types for all of the above |
| Event | Fires when |
|---|---|
network:created |
A new network is added to the workspace |
network:deleted |
A network is removed |
network:switched |
The user navigates to a different network |
selection:changed |
Node or edge selection changes |
layout:started |
A layout algorithm begins |
layout:completed |
A layout algorithm finishes |
style:changed |
A visual style property changes |
data:changed |
Node or edge attribute data changes |
Outside React components, the same APIs are available via window.CyWebApi:
window.addEventListener('cywebapi:ready', () => {
const api = window.CyWebApi
const result = api.workspace.getCurrentNetworkId()
// ...
})Note:
window.CyWebApidoes not includeresourceor per-appcontextMenu. Those are only available insidemount()viacontext.apisor viauseAppContext().
| Example | Best for | Details |
|---|---|---|
| project-template/ | Your first app — panel, menu action, and context menu | README |
| hello-world/ | Full API coverage — 13 examples covering all APIs | README |
| network-statistics/ | Non-React — graph traversal, event-driven logging | README |
| network-workflows/ | CX2 import, Jupyter integration, menu workflows | README |
Recommended reading order: project-template → hello-world → network-statistics → network-workflows
Install the types package for IDE support:
npm install --save-dev @cytoscape-web/api-typesReference the package's bundled declarations from your tsconfig.json so
TypeScript resolves the cyweb/* ambient modules:
{
"include": ["src/**/*"],
"compilerOptions": {
"moduleResolution": "bundler",
"types": ["@cytoscape-web/api-types"]
}
}Listing the package in
typesis what pulls in its ambientcyweb/*declarations. Do not settypeRoots: the example apps used to point it at./node_modules/@types, a directory that does not exist in a workspace, and setting it suppresses the default lookup that actually finds the types. See any of the example apps'tsconfig.jsonfor a working reference.
npm run dev # run all workspaces concurrently
npm run dev:hello-world # run one app
npm run dev:network-statistics
npm run dev:network-workflows
npm run dev:project-template
npm run dev:claude-bridge # MCP bridge (optional, internal tool)
npm run build # build all workspaces
npm run deploy # build and copy each workspace's dist/ into docs/npm run verify:federation # the built dist/ has the right federation shape
npm run preflight:host -- <hostUrl> # the host publishes a usable descriptor
npm run preflight:apps -- <hostUrl> <appsBase> # the PUBLISHED apps load in that hostThe three cover different layers, and the gap between the first two is why the
third exists. verify:federation reads dist/, preflight:host reads the
host — so a fault in the serving layer between them is invisible to both. That is not
hypothetical: GitHub Pages ran this repo's docs/ through Jekyll, which drops
_-prefixed paths, and silently 404'd the _virtual_mf-* chunk every app
imports first while both other checks stayed green.
preflight:apps loads each published app through a real dynamic import()
inside a real host page, so transitive chunk fetches, CORS and MIME are the real
ones. -- --selftest proves it can still fail.
Older examples used direct store imports. They still work, but new apps should
use the App API hooks instead — they return ApiResult<T> and provide a
stable, documented contract. See
Architecture Overview → Host Exposes Reference
for the full list of legacy cyweb/*Store exposes.
| Deprecated pattern | Recommended replacement |
|---|---|
useNetworkStore |
useNetworkApi |
useTableStore |
useTableApi |
useWorkspaceStore |
useWorkspaceApi |