Process & Methodology

Creating Apps

This document provides step-by-step instructions for creating a new FlowState sub-app and integrating it into the Next.js and Electron container applications.

Prerequisites

  • FlowState CLI installed (flowstate command available)
  • Working FlowState monorepo environment
  • Yarn package manager

Overview

Creating a new FlowState app involves:

  1. Scaffolding the app using the CLI
  2. Renaming boilerplate references
  3. Updating exports to follow naming conventions
  4. Integrating into Next.js container
  5. Integrating into Electron container
  6. Configuring the plugin manifest
  7. Verification

Step 1: Create the App Using CLI

Run the FlowState CLI to scaffold the new app:

flowstate create-app <app-name> --verbose

Example:

flowstate create-app process --verbose

This creates the app at packages/apps/flowstate-app-<app-name>/ with:

  • Basic React app structure
  • Plugin definition
  • Tailwind CSS configuration
  • TypeScript configuration
  • Vite build setup

Expected output:

Creating new FlowState app: <app-name>
  ✓ Template copied
  ✓ Configuration files updated
  ✓ Dependencies installed
  ✓ Build passed
  ✓ Typecheck passed
  ✓ Successfully created: flowstate-app-<app-name>

Step 2: Rename Boilerplate References

The scaffolded app contains "Boilerplate" placeholder text that must be replaced.

2.1 Search for Boilerplate References

grep -r "Boilerplate\|boilerplate" packages/apps/flowstate-app-<app-name>/

2.2 Update All Files

Replace "Boilerplate" with your app name in these files:

FileChanges
src/components/Header.tsxUpdate comment and title prop
src/components/Sidebar.tsxUpdate comment and title prop
src/pages/HomePage.tsxUpdate welcome text, description, and icon
src/pages/UsersPageWrapper.tsxUpdate comment
src/pages/OrgsPageWrapper.tsxUpdate comment
src/pages/WorkspacesPageWrapper.tsxUpdate comment
src/plugin.tsUpdate comment, name, and icon
src/App.tsxUpdate comment
tailwind.config.jsUpdate path comment
postcss.config.jsUpdate path comment

Example for Header.tsx:

/**
 * <AppName> App Header - Uses shared Header from flowstate-app-framework
 */
// ...
<SharedHeader
  title="<AppName>"
  standalone={standalone}
  colorMode="light"
/>

2.3 Choose an Appropriate Icon

Update the icon import in src/plugin.ts and src/pages/HomePage.tsx:

import { Workflow } from 'lucide-react';  // Choose appropriate icon

Available icons: https://lucide.dev/icons/


Step 3: Update Exports (Naming Convention)

Edit src/index.ts to export the app component with a named export following the pattern <AppName>App:

// Main exports
export { <appName>Plugin } from './plugin';
export { App as <AppName>App } from './App';
export type { AppProps as <AppName>AppProps } from './App';

// Components
export { Sidebar } from './components/Sidebar';
export { Header } from './components/Header';

// Version
export const version = '1.0.0';

Example for Process app:

export { processPlugin } from './plugin';
export { App as ProcessApp } from './App';
export type { AppProps as ProcessAppProps } from './App';

Step 4: Integrate into Next.js Container

4.1 Add Route (packages/flowstate-app-nextjs/src/config/routes.tsx)

Add lazy import at the top with other imports:

const <AppName>App = lazy(() => import('@epicdm/flowstate-app-<app-name>').then((m) => ({ default: m.<AppName>App })));

Add route inside <RoutesComponent>:

<RouteComponent
  path="/apps/<app-name>/*"
  element={<<AppName>App config={{ useHostRouter: true, basePath: '/apps/<app-name>' }} />}
/>

4.2 Add Tailwind Content (packages/flowstate-app-nextjs/tailwind.config.js)

Add to the content array:

'../apps/flowstate-app-<app-name>/src/**/*.{js,jsx,ts,tsx}',

4.3 Update Next.js Config (packages/flowstate-app-nextjs/next.config.mjs)

Add to transpilePackages array:

'@epicdm/flowstate-app-<app-name>',

Add to config.resolve.alias object:

'@epicdm/flowstate-app-<app-name>$': resolve(__dirname, '../apps/flowstate-app-<app-name>/src/index.ts'),

Add to config.resolve.modules.push() call:

resolve(__dirname, '../apps/flowstate-app-<app-name>/src'),

4.4 Add Dependency (packages/flowstate-app-nextjs/package.json)

Add to dependencies:

"@epicdm/flowstate-app-<app-name>": "workspace:*",

4.5 Add Manifest (packages/flowstate-app-nextjs/src/components/providers/ManifestLoader.tsx)

Add import:

import <appName>Manifest from '../../../../apps/flowstate-app-<app-name>/flowstate.json';

Add to manifests array:

const manifests = [
  // ... existing manifests
  <appName>Manifest,
] as const;

Step 5: Integrate into Electron Container

5.1 Add App Component (packages/flowstate-app-electron/src/renderer/config/apps.ts)

Add to appComponents object:

<app-name>: lazy(() =>
  import('@epicdm/flowstate-app-<app-name>').then((m) => ({ default: m.<AppName>App }))
),

5.2 Add Route (packages/flowstate-app-electron/src/renderer/routes/index.tsx)

Add variable assignment with other apps:

const <AppName>App = appComponents.<app-name>;

Add route inside <Routes>:

<Route
  path="/apps/<app-name>/*"
  element={<<AppName>App config={{ useHostRouter: true, basePath: '/apps/<app-name>' }} />}
/>

5.3 Add Tailwind Content (packages/flowstate-app-electron/tailwind.config.js)

Add to the content array:

'../apps/flowstate-app-<app-name>/src/**/*.{js,jsx,ts,tsx}',

5.4 Update Vite Config (packages/flowstate-app-electron/vite.config.ts)

Add to resolve.alias object:

'@epicdm/flowstate-app-<app-name>$': path.resolve(__dirname, '../apps/flowstate-app-<app-name>/src/index.ts'),
'@epicdm/flowstate-app-<app-name>': path.resolve(__dirname, '../apps/flowstate-app-<app-name>/src'),

Add to optimizeDeps.exclude array:

'@epicdm/flowstate-app-<app-name>',

Add to ssr.noExternal array:

'@epicdm/flowstate-app-<app-name>',

5.5 Add Dependency (packages/flowstate-app-electron/package.json)

Add to dependencies:

"@epicdm/flowstate-app-<app-name>": "workspace:*",

5.6 Add Manifest (packages/flowstate-app-electron/src/renderer/providers/ManifestLoader.tsx)

Add import:

import <appName>Manifest from '../../../../apps/flowstate-app-<app-name>/flowstate.json';

Add to manifests array:

const manifests = [
  // ... existing manifests
  <appName>Manifest,
] as const;

5.7 Create Type Declaration (packages/flowstate-app-electron/src/types/epic-flow__flowstate-app-<app-name>.d.ts)

// Type declaration for @epicdm/flowstate-app-<app-name>
declare module '@epicdm/flowstate-app-<app-name>' {
  import { ComponentType } from 'react';

  export const <appName>Plugin: any;
  export const <AppName>App: ComponentType<any>;
}

Note: Using ComponentType<any> instead of any ensures TypeScript recognizes this as a React component that can accept props.


Step 6: Configure Plugin Manifest

Update packages/apps/flowstate-app-<app-name>/flowstate.json with complete configuration:

{
  "$schema": "https://flowstate.dev/schemas/plugin-manifest-v1.json",
  "id": "<app-name>",
  "displayName": "<App Name>",
  "version": "1.0.0",
  "description": "<App description>",
  "publisher": "flowstate",
  "main": "./dist/index.js",
  "browser": "./dist/index.mjs",
  "icon": "<lucide-icon-name>",
  "color": "#6366f1",
  "route": "/apps/<app-name>",
  "order": 50,
  "basePath": "/<app-name>",
  "category": "technical",
  "activationEvents": ["onRoute:/<app-name>"],
  "permissions": [],
  "requiresServer": false,
  "contributes": {
    "commands": [],
    "menus": [],
    "keybindings": []
  }
}

Manifest fields:

FieldDescription
idUnique app identifier (kebab-case)
displayNameHuman-readable name shown in UI
iconLucide icon name (e.g., "workflow", "folder-kanban")
colorHex color for app branding
orderSidebar position (lower = higher)
categoryApp category: "business", "technical", "other"

Step 7: Install Dependencies and Build

# Install workspace dependencies
yarn install

# Build the new app
yarn workspace @epicdm/flowstate-app-<app-name> build

# Verify typecheck passes
yarn workspace @epicdm/flowstate-app-<app-name> typecheck
yarn workspace @epicdm/flowstate-app-nextjs typecheck
yarn workspace @epicdm/flowstate-app-electron typecheck

Step 8: Verification

8.1 Verify No Boilerplate References Remain

grep -r "Boilerplate\|boilerplate" packages/apps/flowstate-app-<app-name>/
# Should return no matches

8.2 Test in Next.js Container

yarn dev:nextjs

Navigate to: http://localhost:4000/apps/<app-name>/home

Verify:

  • [ ] App loads without errors
  • [ ] App name displays correctly in header/sidebar
  • [ ] App appears in launcher grid
  • [ ] Navigation works correctly

8.3 Test HMR (Hot Module Replacement)

  1. Make a change to a component in the app
  2. Verify the change appears in the browser without full reload

Quick Reference Checklist

Files to Create/Modify for New App

App Package (packages/apps/flowstate-app-<app-name>/):

  • [ ] Created via flowstate create-app
  • [ ] src/index.ts - Export <AppName>App
  • [ ] src/plugin.ts - Update name, icon
  • [ ] src/components/Header.tsx - Update title
  • [ ] src/components/Sidebar.tsx - Update title
  • [ ] src/pages/HomePage.tsx - Update content
  • [ ] src/pages/*PageWrapper.tsx - Update comments
  • [ ] flowstate.json - Complete manifest
  • [ ] tailwind.config.js - Update path comment
  • [ ] postcss.config.js - Update path comment

Next.js Container (packages/flowstate-app-nextjs/):

  • [ ] src/config/routes.tsx - Add import and route
  • [ ] tailwind.config.js - Add content path
  • [ ] next.config.mjs - Add transpile, alias, modules
  • [ ] package.json - Add dependency
  • [ ] src/components/providers/ManifestLoader.tsx - Add manifest

Electron Container (packages/flowstate-app-electron/):

  • [ ] src/renderer/config/apps.ts - Add lazy import
  • [ ] src/renderer/routes/index.tsx - Add variable and route
  • [ ] tailwind.config.js - Add content path
  • [ ] vite.config.ts - Add alias, exclude, noExternal
  • [ ] package.json - Add dependency
  • [ ] src/renderer/providers/ManifestLoader.tsx - Add manifest
  • [ ] src/types/epic-flow__flowstate-app-<app-name>.d.ts - Create type declaration (use ComponentType<any>)

Troubleshooting

App not appearing in sidebar

  • Verify manifest is imported in ManifestLoader.tsx for both containers
  • Check flowstate.json has valid route and order fields
  • Restart dev server after manifest changes

TypeScript errors after integration

  • Run yarn workspace @epicdm/flowstate-app-<app-name> build to generate dist files
  • Verify type declaration file exists for Electron container
  • Check all aliases are correctly configured
  • If error mentions "Property 'config' does not exist on type 'IntrinsicAttributes'", ensure the type declaration uses ComponentType<any> instead of any for the app export

Styles not applying

  • Verify content path added to both container tailwind.config.js files
  • Restart dev server after Tailwind config changes

HMR not working

  • Verify source aliases point to src/index.ts (not dist/)
  • Check optimizeDeps.exclude includes the app package
  • Check ssr.noExternal includes the app package
Previous
Product-Driven Development