App Boilerplate

Getting Started

Getting Started with Boilerplate App

The Boilerplate App serves as the source template for the FlowState CLI's create-app command. This guide walks you through creating a new app and integrating it into the FlowState ecosystem.

Prerequisites

Before creating a new app, ensure you have:

  • FlowState CLI installed (flowstate command available)
  • Working FlowState monorepo environment
  • Yarn package manager
  • Node.js 18+

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.

Search for Boilerplate References

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

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

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

Add Route

Edit packages/flowstate-app-nextjs/src/config/routes.tsx:

// Add lazy import at the top
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>' }} />}
/>

Add Tailwind Content

Edit packages/flowstate-app-nextjs/tailwind.config.js:

content: [
  // ... existing paths
  '../apps/flowstate-app-<app-name>/src/**/*.{js,jsx,ts,tsx}',
]

Update Next.js Config

Edit 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'),

Add Dependency

Edit packages/flowstate-app-nextjs/package.json:

"dependencies": {
  "@epicdm/flowstate-app-<app-name>": "workspace:*"
}

Add Manifest

Edit packages/flowstate-app-nextjs/src/components/providers/ManifestLoader.tsx:

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

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

Step 5: Integrate into Electron Container

Add App Component

Edit packages/flowstate-app-electron/src/renderer/config/apps.ts:

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

Add Route

Edit packages/flowstate-app-electron/src/renderer/routes/index.tsx:

// Add variable assignment
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>' }} />}
/>

Add Tailwind Content

Edit packages/flowstate-app-electron/tailwind.config.js:

content: [
  // ... existing paths
  '../apps/flowstate-app-<app-name>/src/**/*.{js,jsx,ts,tsx}',
]

Update Vite Config

Edit 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>',

Add Dependency

Edit packages/flowstate-app-electron/package.json:

"dependencies": {
  "@epicdm/flowstate-app-<app-name>": "workspace:*"
}

Add Manifest

Edit packages/flowstate-app-electron/src/renderer/providers/ManifestLoader.tsx:

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

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

Create Type Declaration

Create packages/flowstate-app-electron/src/types/epic-flow__flowstate-app-<app-name>.d.ts:

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

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

Step 6: Configure Plugin Manifest

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

{
  "$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

Verify No Boilerplate References Remain

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

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

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

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", ensure the type declaration uses ComponentType<any>

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

Next Steps

Once your app is created and integrated:

Previous
Overview