
Fusion Developer Portal
- 759 installs
- 1 repo stars
- Updated August 4, 2026
- equinor/fusion-skills
fusion-developer-portal is a Claude Code skill that scaffolds and configures Equinor Fusion portal shells with micro-frontend routing, module loading, and shared header chrome for developers building enterprise Fusion Fr
About
fusion-developer-portal is an Equinor Fusion Framework skill for portal shell development under MIT license. It guides scaffolding, module configuration, app loading, routing, header and context selector integration, analytics, telemetry, manifest setup, ffc portal dev workflows, and deployment using Fusion Framework CLI portal commands. Developers reach for fusion-developer-portal when creating a portal that embeds multiple micro-frontend applications inside shared UI chrome rather than building standalone app features. The skill explicitly excludes app-level React feature work, backend service changes, and Fusion Help Center integration, pointing those tasks to fusion-app-react-dev and other skills.
- Scaffolds complete Fusion portal shells using the Fusion Framework CLI
- Configures portal modules, app loading, routing, header and context selector integration
- Handles portal manifest, analytics, telemetry and deployment workflows
- Guides embedding of multiple apps inside shared portal chrome
- Requires @equinor/fusion-framework-cli; pairs with fusion-app-react-dev for app-level work
Fusion Developer Portal by the numbers
- 759 all-time installs (skills.sh)
- Ranked #460 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/equinor/fusion-skills --skill fusion-developer-portalAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 759 |
|---|---|
| repo stars | ★ 1 |
| Last updated | August 4, 2026 |
| Repository | equinor/fusion-skills ↗ |
How do you scaffold a Fusion micro-frontend portal shell?
Scaffold, configure, and develop Fusion portal shells that host and route multiple micro-frontend applications inside shared UI chrome.
Who is it for?
Enterprise frontend developers building Equinor Fusion portal hosts that load and route multiple micro-frontend applications in shared chrome.
Skip if: Standalone React feature development inside a single Fusion app, backend API changes, or teams outside the Fusion Framework ecosystem.
When should I use this skill?
User asks to create a Fusion portal, configure portal modules, set up portal routing, embed apps in portal chrome, or run ffc portal dev deployment.
What you get
Configured Fusion portal shell with module manifest, embedded app routes, header integration, and deployment-ready portal project.
- portal shell project
- module manifest
- embedded app routing configuration
Files
Fusion Developer Portal
Guide development of Fusion portal shells — host applications that load, route, and render Fusion apps inside shared chrome (header, context selector, navigation).
When to use
- Scaffold new Fusion portal
portal.manifest.tsorffc portal devquestions- Configure portal-level modules (telemetry, analytics, navigation, services, app module)
- Build custom app loader or portal shell
- Add portal-level routing (
/apps/:appKey/*) Apploadercomponent oruseApploaderhook for embedding apps- Wire up portal header, context selector, or bookmark side sheet
- Understand how portals load/initialize Fusion apps at runtime
- Add portal-level analytics or telemetry
- Portal deployment (
ffc portal build,ffc portal upload)
When not to use
- App-level feature development →
fusion-app-react-dev - Backend service changes → separate repo
- Fusion Help Center integration →
fusion-help-integration - Skill authoring →
fusion-skill-authoring - Issue authoring →
fusion-issue-authoring
Required inputs
Mandatory
- What to build: scaffold a portal, add a feature, configure a module, or understand portal architecture
- Portal context: new from scratch or modifying existing
Conditional
- Portal name/ID when scaffolding
- MSAL client ID and service discovery URL when configuring auth
- Specific modules to enable (analytics, telemetry, bookmarks, feature flags, AG Grid)
- Whether the portal needs a custom app loader or the default
Apploadersuffices
Instructions
Step 1 — Classify the portal task
Determine what the user needs:
- Scaffold new portal → step 2
- Configure portal modules → step 3
- App loading and routing → step 4
- Portal chrome (header, context, bookmarks) → step 5
- Analytics and telemetry → step 6
- Build and deploy → step 7
When Fusion MCP is available, prefer mcp_fusion_search_framework with queries like "portal manifest definePortalManifest ffc portal" or "createFrameworkProvider PortalModuleInitiator portal configure" for latest API surface. Label guidance not confirmed by MCP as fallback.
Step 2 — Scaffold a new portal
Use the Fusion Framework CLI:
mkdir my-fusion-portal && cd my-fusion-portal
pnpm init
pnpm add -D @equinor/fusion-framework-cliCreate the required files:
- `portal.manifest.ts` — portal metadata and configuration:
import { definePortalManifest } from '@equinor/fusion-framework-cli/portal';
export default definePortalManifest((env, { base }) => ({
name: 'my-portal',
version: '1.0.0',
entry: './src/index.tsx',
}));- `portal.schema.ts` (optional) — configuration validation schema
- `src/index.tsx` — portal entry point (see references/portal-architecture.md)
Start the dev server:
pnpm fusion-framework-cli portal dev
# or: ffc portal devStep 3 — Configure portal modules
Portal-level configuration uses FrameworkConfigurator (not AppModuleInitiator like apps). Enable modules in your configure callback:
import type { FrameworkConfigurator } from '@equinor/fusion-framework';
import { enableAppModule } from '@equinor/fusion-framework-module-app';
import { enableNavigation } from '@equinor/fusion-framework-module-navigation';
import { enableAnalytics } from '@equinor/fusion-framework-module-analytics';
const configure = (configurator: FrameworkConfigurator) => {
enableAppModule(configurator);
enableNavigation(configurator, '/');
enableAnalytics(configurator, { /* ... */ });
};Key difference from app configuration: the portal configures FrameworkConfigurator and its modules are hoisted to all child apps. MSAL/auth is configured at portal level and inherited by apps.
See references/portal-architecture.md for full module configuration and portal-analytics cookbook reference.
Step 4 — App loading and routing
The portal routes /apps/:appKey/* to an app loader. Two approaches:
Simple embed — use the built-in Apploader component:
import { Apploader } from '@equinor/fusion-framework-react-app/apploader';
<Apploader appKey="my-app" />Warning: Apploader is an experimental POC. Embedded apps may have routing and context issues. Best for simple apps like PowerBI or PowerApps views.Custom app loader — for full control over loading states, error handling, and mounting. Use useFramework<[AppModule]>(), observe fusion.modules.app.current$, and call app.initialize(). See references/portal-architecture.md for the annotated custom AppLoader pattern.
Portal routing typically uses react-router-dom via the navigation module:
const Router = () => {
const framework = useFramework();
const router = framework.modules.navigation.router;
return <RouterProvider router={router} />;
};Step 5 — Portal chrome
Portal-level UI typically includes:
- Header — top bar with Fusion logo, context selector, bookmarks, person settings
- Context selector — wired to app context module via
useCurrentContextoruseContextSelector - Bookmark side sheet — toggled via navigation module
- App navigation — sidebar or top-level nav for switching apps
Apps don't build their own header — they receive it from the portal shell.
Step 6 — Analytics and telemetry
Portal-level analytics capture app lifecycle events. Enable with adapters and collectors:
import { enableAnalytics } from '@equinor/fusion-framework-module-analytics';
import { ConsoleAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/adapters';
import { AppLoadedCollector, AppSelectedCollector } from '@equinor/fusion-framework-module-analytics/collectors';
enableAnalytics(configurator, (builder) => {
builder.addAdapter(new ConsoleAnalyticsAdapter());
builder.addCollector(new AppLoadedCollector());
builder.addCollector(new AppSelectedCollector());
});For telemetry (OpenTelemetry / Application Insights), see references/portal-architecture.md.
Step 7 — Build and deploy
# Build the portal template
ffc portal build
# Upload to the Fusion portal service (authenticate via `az login` or FUSION_TOKEN env var)
ffc portal upload --portal-id <id>
# Tag a specific version
ffc portal tag --portal-id <id> --tag latest --version <version>Never paste tokens directly into commands. Useaz loginfor interactive auth or setFUSION_TOKENenv var for CI pipelines.
Step 8 — Validate
1. ffc portal dev starts without errors 2. Apps load at /apps/:appKey 3. Context selector and header render properly 4. TypeScript compiles with no errors 5. Analytics events fire on app load/select (if enabled)
Expected output
- Working portal shell with app loading, routing, and chrome
- Portal-level module configuration with correct
FrameworkConfiguratorusage - Loading and error states handled for app initialization
- Brief summary of what was created or changed
Helper agents
Same companion infrastructure as fusion-app-react-dev:
- `fusion-research` — source-backed Fusion ecosystem research when portal behavior is uncertain
- `fusion-code-conventions` — naming, TSDoc, and code style checks
When Fusion MCP is available, prefer mcp_fusion_search_framework for portal-specific lookups.
Safety & constraints
- MSAL is portal-level — never configure auth in apps; it is hoisted from the portal
- Don't invent portal APIs — only reference APIs confirmed by MCP or documented in references
- `Apploader` is experimental — always mention routing/context limitations when advising on app embedding
- No secrets in source — MSAL client IDs must come from environment variables, not hardcoded
- Don't modify the production Fusion portal — this skill covers custom portal development only
- Conventional commits for all changes
Changelog
0.0.2 - 2026-05-07
patch
- Drop articles, filler, hedging from SKILL.md activation body
- Compress portal-architecture reference
0.0.1 - 2026-03-23
patch
- Guides scaffolding portals with the Fusion Framework CLI (
ffc portal dev/build/upload) - Documents portal-level module configuration (
FrameworkConfiguratorvsAppModuleInitiator) - Covers app loading (Apploader component, useApploader hook, custom AppLoader with RxJS)
- Includes portal routing, chrome (header, context selector), analytics, and telemetry
- Reference file with full portal architecture patterns from framework cookbooks
resolves equinor/fusion-core-tasks#752
Portal Architecture Reference
Fusion portal patterns: entry point, module config, custom app loading, lifecycle.
Portal entry point
Portal creates framework provider, renders shell. From portal-analytics cookbook:
import { createElement, type FC, lazy, Suspense } from 'react';
import { createRoot } from 'react-dom/client';
import { createFrameworkProvider, type Fusion } from '@equinor/fusion-framework-react';
import { type ComponentRenderArgs, frameworkConfig, type PortalModuleInitiator } from './frameworkConfig';
import { Router } from './Router';
const createPortal = <TRef extends Fusion = Fusion>(
el: HTMLElement,
args: ComponentRenderArgs<TRef>,
) => {
const FrameworkProvider = createFrameworkProvider(
frameworkConfig as PortalModuleInitiator<TRef>,
args,
);
const root = createRoot(el);
root.render(
<FrameworkProvider>
<Router />
</FrameworkProvider>,
);
return () => root.unmount();
};
export default createPortal;Module configuration
Portal uses FrameworkConfigurator, not AppModuleInitiator. Config hoisted to child apps.
import type { FrameworkConfigurator, Fusion } from '@equinor/fusion-framework';
import { enableAppModule } from '@equinor/fusion-framework-module-app';
import { enableAnalytics, type AnalyticsEvent } from '@equinor/fusion-framework-module-analytics';
import { ConsoleAnalyticsAdapter, FusionAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/adapters';
import { AppLoadedCollector, AppSelectedCollector, ContextSelectedCollector } from '@equinor/fusion-framework-module-analytics/collectors';
import { enableTelemetry } from '@equinor/fusion-framework-module-telemetry';
export type PortalModuleInitiator<TRef extends Fusion = Fusion> = (
configurator: FrameworkConfigurator,
ref?: TRef,
) => void | Promise<void>;
export type ComponentRenderArgs<TRef extends Fusion = Fusion> = {
fusion: TRef;
env: { basename: string };
};
export const frameworkConfig: PortalModuleInitiator = (configurator) => {
// App module — required for loading child apps
enableAppModule(configurator);
// Analytics with adapters and collectors
enableAnalytics(configurator, (builder) => {
builder.addAdapter(new ConsoleAnalyticsAdapter());
builder.addAdapter(new FusionAnalyticsAdapter());
builder.addCollector(new AppLoadedCollector());
builder.addCollector(new AppSelectedCollector());
builder.addCollector(new ContextSelectedCollector());
});
// Telemetry (OpenTelemetry)
enableTelemetry(configurator, {
attachConfiguratorEvents: true,
});
};Key difference from app configuration
| Concern | Portal (FrameworkConfigurator) | App (AppModuleInitiator) |
|---|---|---|
| Auth / MSAL | Configured here, hoisted to apps | Inherited — do NOT configure in apps |
| App module | enableAppModule(configurator) | Not needed (portal handles it) |
| Navigation | enableNavigation(configurator, '/') | enableNavigation(configurator, basename) |
| Service discovery | Configured at portal level | Inherited from portal |
| HTTP clients | Portal-wide clients | App-specific clients via configurator.http |
Portal routing
Portals use react-router-dom via framework navigation module. Standard pattern routes /apps/:appKey/* to app loader:
import { Outlet, RouterProvider, type RouterProviderProps, useParams } from 'react-router-dom';
import { AppLoader } from './AppLoader';
import { Header } from './components/Header';
import { useFramework } from '@equinor/fusion-framework-react';
const Root = () => (
<>
<Header />
<Outlet />
</>
);
const AppRoute = () => {
const { appKey } = useParams<{ appKey: string }>();
return appKey ? <AppLoader appKey={appKey} /> : null;
};
export const Router = () => {
const framework = useFramework();
// Router is created from the navigation module
return <RouterProvider router={framework.modules.navigation.router} />;
};Custom app loader
Fine-grained control over loading, error handling, mounting. From portal-analytics cookbook:
import { useEffect, useId, useMemo, useRef, useState } from 'react';
import { Subscription } from 'rxjs';
import { last } from 'rxjs/operators';
import { useFramework } from '@equinor/fusion-framework-react';
import { useObservableState } from '@equinor/fusion-observable/react';
import type { AppModule } from '@equinor/fusion-framework-module-app';
export const AppLoader = (props: { readonly appKey: string }) => {
const { appKey } = props;
const fusion = useFramework<[AppModule]>();
const ref = useRef<HTMLElement>(null);
const applicationContentId = useId();
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<Error | undefined>();
const { value: currentApp } = useObservableState(
useMemo(() => fusion.modules.app.current$, [fusion.modules.app]),
);
useEffect(() => {
fusion.modules.app.setCurrentApp(appKey);
}, [appKey, fusion]);
useEffect(() => {
if (!currentApp) return;
setLoading(true);
setError(undefined);
const subscription = new Subscription();
subscription.add(
currentApp
.initialize()
.pipe(last())
.subscribe({
next: ({ manifest, script, config }) => {
const basename = `/apps/${appKey}`;
const el = document.createElement('div');
if (!ref.current) throw Error('Missing application mounting point');
ref.current.appendChild(el);
const render = script.renderApp ?? script.default;
subscription.add(render(el, { fusion, env: { basename, config, manifest } }));
subscription.add(() => el.remove());
},
complete: () => setLoading(false),
error: (err) => {
setLoading(false);
setError(err);
},
}),
);
return () => subscription.unsubscribe();
}, [fusion, currentApp]);
if (error) {
return <div><h2>Failed to load application</h2><pre>{error.message}</pre></div>;
}
return (
<section id={applicationContentId} ref={ref} style={{ display: 'contents' }}>
{loading && <div>Loading Application…</div>}
</section>
);
};Key points
useFramework<[AppModule]>()accesses the framework with the app module typedfusion.modules.app.setCurrentApp(appKey)triggers the loading pipelinefusion.modules.app.current$emits the currentAppinstanceapp.initialize()returns an observable emitting{ manifest, script, config }when readyscript.renderApp ?? script.defaultextracts the render function- Subscription cleanup is critical — always unsubscribe to prevent memory leaks
Apploader component (simple embed)
From @equinor/fusion-framework-react-app/apploader. Quick way to embed a child app:
import { Apploader } from '@equinor/fusion-framework-react-app/apploader';
<Apploader appKey="my-app" />For custom loading/error UI, use the useApploader hook:
useApploader({ appKey }: { appKey: string }): {
loading: boolean;
error: Error | undefined;
appRef: React.RefObject<HTMLDivElement | null>;
}Warning: Apploader is experimental. Embedded apps may have routing, context, and framework issues. Best for simple apps (PowerBI, PowerApps).App initialization lifecycle
How the portal loads any Fusion app:
1. Manifest fetch — AppModuleProvider.getAppManifest(appKey) retrieves metadata via service discovery 2. Asset import — App Proxy fetches the JS bundle (handles auth since import() can't carry tokens) 3. Module configuration — app's configure callback enables and configures framework modules 4. Module initialization — configurator generates config and creates module instances 5. Render — app's renderApp() receives DOM element, framework instance, basename, config, manifest
App class observables
| Observable | Emits |
|---|---|
manifest$ | AppManifest when resolved |
config$ | AppConfig when resolved |
modules$ | Imported AppScriptModule |
instance$ | Initialized AppModulesInstance |
status$ | Set of in-progress action types |
Portal manifest
import { definePortalManifest } from '@equinor/fusion-framework-cli/portal';
export default definePortalManifest((env, { base }) => ({
name: 'my-portal',
version: '1.0.0',
entry: './src/index.tsx',
}));CLI commands
| Command | Description |
|---|---|
ffc portal dev | Start dev server with hot reload |
ffc portal build | Build portal template with Vite |
ffc portal manifest | Generate or validate portal manifest |
ffc portal schema | Generate or validate config schema |
ffc portal upload | Upload portal build to Fusion service |
ffc portal tag | Tag a portal version (e.g., latest) |
Dev portal architecture
The built-in @equinor/fusion-framework-dev-portal provides:
- `render` — entry point with React root, theme, framework, people-resolver providers
- `configure` — all framework modules (telemetry, navigation, bookmarks, feature flags, analytics, AG Grid, services)
- `Router` — routes
/apps/:appKey/*to the app loader - `AppLoader` — resolves, initializes, and mounts apps by key with loading/error states
- `Header` — top bar with Fusion logo, context selector, bookmark toggle, person settings
- `ContextSelector` — wired to current app's context module
- `useAppContextNavigation` — syncs URL pathname with context changes
This is for local development only. Production portals are deployed separately.
Environment variables
| Variable | Required | Description |
|---|---|---|
FUSION_MSAL_CLIENT_ID | Yes | Azure AD app client ID |
FUSION_SPA_SERVICE_DISCOVERY_URL | Yes | Service discovery endpoint |
FUSION_SPA_PORTAL_ID | No | Portal ID for service resolution |
FUSION_AG_GRID_KEY | No | AG Grid enterprise license key |
MCP query examples
mcp_fusion_search_framework → "definePortalManifest portal manifest ffc portal CLI"
mcp_fusion_search_framework → "createFrameworkProvider PortalModuleInitiator portal configure modules"
mcp_fusion_search_framework → "AppLoader AppModule setCurrentApp current$ app initialize"
mcp_fusion_search_framework → "enableAnalytics AppLoadedCollector portal analytics telemetry"
mcp_fusion_search_docs → "portal development custom portal architecture deployment"Source references
Related skills
How it compares
Pick fusion-developer-portal for portal host shells and fusion-app-react-dev for feature work inside individual Fusion React applications.
FAQ
What does fusion-developer-portal cover?
fusion-developer-portal guides Fusion portal shell scaffolding, module configuration, app loading, routing, header and context integration, analytics, and deployment using Fusion Framework CLI portal commands for embedded micro-frontends.
When should I not use fusion-developer-portal?
fusion-developer-portal excludes app-level React feature development, backend service changes, and Fusion Help Center integration. Use fusion-app-react-dev for single-app feature work inside the Fusion ecosystem.