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 (
flowstatecommand available) - Working FlowState monorepo environment
- Yarn package manager
Overview
Creating a new FlowState app involves:
- Scaffolding the app using the CLI
- Renaming boilerplate references
- Updating exports to follow naming conventions
- Integrating into Next.js container
- Integrating into Electron container
- Configuring the plugin manifest
- 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:
| File | Changes |
|---|---|
src/components/Header.tsx | Update comment and title prop |
src/components/Sidebar.tsx | Update comment and title prop |
src/pages/HomePage.tsx | Update welcome text, description, and icon |
src/pages/UsersPageWrapper.tsx | Update comment |
src/pages/OrgsPageWrapper.tsx | Update comment |
src/pages/WorkspacesPageWrapper.tsx | Update comment |
src/plugin.ts | Update comment, name, and icon |
src/App.tsx | Update comment |
tailwind.config.js | Update path comment |
postcss.config.js | Update 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:
| Field | Description |
|---|---|
id | Unique app identifier (kebab-case) |
displayName | Human-readable name shown in UI |
icon | Lucide icon name (e.g., "workflow", "folder-kanban") |
color | Hex color for app branding |
order | Sidebar position (lower = higher) |
category | App 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)
- Make a change to a component in the app
- 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 (useComponentType<any>)
Troubleshooting
App not appearing in sidebar
- Verify manifest is imported in
ManifestLoader.tsxfor both containers - Check
flowstate.jsonhas validrouteandorderfields - Restart dev server after manifest changes
TypeScript errors after integration
- Run
yarn workspace @epicdm/flowstate-app-<app-name> buildto 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 ofanyfor the app export
Styles not applying
- Verify content path added to both container
tailwind.config.jsfiles - Restart dev server after Tailwind config changes
HMR not working
- Verify source aliases point to
src/index.ts(notdist/) - Check
optimizeDeps.excludeincludes the app package - Check
ssr.noExternalincludes the app package