App Boilerplate
Template Structure
Template Structure
This document provides a comprehensive guide to the App Boilerplate template structure, explaining each file and directory, how components work together, and best practices for customization.
Directory Structure
flowstate-app-boilerplate/
├── .flowstate/
│ ├── config.json # FlowState project configuration
│ └── docs/ # Package documentation
├── src/
│ ├── App.tsx # Main app component with routing
│ ├── index.ts # Package exports
│ ├── plugin.ts # Plugin registration for host container
│ ├── standalone.tsx # Standalone entry point for development
│ ├── index.css # Global CSS with Tailwind directives
│ ├── components/
│ │ ├── Header.tsx # App header using shared framework component
│ │ └── Sidebar.tsx # App sidebar with navigation links
│ └── pages/
│ ├── HomePage.tsx # Landing page with getting started guide
│ ├── UsersPageWrapper.tsx # Standalone: user management
│ ├── OrgsPageWrapper.tsx # Standalone: organization management
│ └── WorkspacesPageWrapper.tsx # Standalone: workspace management
├── flowstate.json # App manifest with commands and permissions
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration
├── tsup.config.ts # Build configuration
├── vite.config.mts # Development server configuration
├── tailwind.config.js # Tailwind CSS configuration
├── postcss.config.js # PostCSS configuration
└── index.html # HTML template for standalone mode
Core Files
App.tsx - Main Application Component
The main React component that sets up routing and layout:
export function App({ config, standalone = false }: AppProps) {
// Router handling for embedded vs standalone mode
const RouterWrapper = useHostRouter ? React.Fragment : BrowserRouter;
return (
<RouterWrapper>
<FlowstateAppContainer config={config}>
<FlowstateAppLayout
sidebar={<Sidebar standalone={standalone} />}
header={<Header standalone={standalone} />}
>
<Routes>
<Route index element={<Navigate to="home" replace />} />
<Route path="home" element={<HomePage />} />
{/* Add your routes here */}
</Routes>
</FlowstateAppLayout>
</FlowstateAppContainer>
</RouterWrapper>
);
}
Key patterns:
- Uses
FlowstateAppContainerfor framework context - Uses
FlowstateAppLayoutfor consistent layout structure - Conditionally wraps with
BrowserRouterbased onuseHostRouterconfig - Passes
standaloneprop to components for mode-specific behavior
plugin.ts - Plugin Registration
Registers the app with the FlowState host container:
export const boilerplatePlugin: AppPlugin = {
// Identification
id: 'boilerplate',
name: 'Boilerplate App',
version: '1.0.0',
description: 'Boilerplate template for FlowState apps',
// UI Metadata
icon: Box as any,
route: '/apps/boilerplate',
color: '#6366f1',
category: 'technical',
// Lazy-loaded component
component: () => import('./App').then(m => ({ default: m.App })),
// Configuration for embedded mode
config: {
useHostRouter: false,
basePath: '/boilerplate',
},
// Lifecycle hooks
onLoad: async (services) => {
console.log('[Plugin] Loaded with services:', services.getServiceNames());
},
onUnload: async () => {
console.log('[Plugin] Unloaded');
},
};
Customization points:
id,name,description- App identityicon,color- Visual appearance in launcherroute- URL path in host applicationcategory- Grouping in app launcherconfig.basePath- Base path for internal routingonLoad/onUnload- Lifecycle hooks for initialization
standalone.tsx - Development Entry Point
Entry point for running the app in standalone mode:
const Container = createFlowstateContainer({
plugins: [
new ServerConfigPlugin({ mode: 'offline', allowAddServer: true }),
new DatabasePlugin({ adapter: 'memory' }),
new OrgWorkspacePlugin({ mode: 'global', allowLocalCreate: true }),
new AuthPlugin(),
],
});
createRoot(root).render(
<React.StrictMode>
<Container>
<BrowserRouter>
<App config={{ useHostRouter: true }} standalone />
</BrowserRouter>
</Container>
</React.StrictMode>
);
Plugin order matters:
ServerConfigPlugin- Server connection configurationDatabasePlugin- RxDB database setup (memory adapter for dev)OrgWorkspacePlugin- Organization/workspace contextAuthPlugin- Authentication (depends on OrgWorkspace)
index.ts - Package Exports
Exports the public API:
// Plugin for host container registration
export { boilerplatePlugin } from './plugin';
// Main app component
export { App } from './App';
export type { AppProps } from './App';
// Components for potential reuse
export { Header } from './components/Header';
export { Sidebar } from './components/Sidebar';
// Version for compatibility checks
export const version = '1.0.0';
Included Components
Header Component
Thin wrapper around the framework's shared Header:
import { Header as SharedHeader } from '@epicdm/flowstate-app-framework';
export function Header({ standalone = false }: HeaderProps) {
return (
<SharedHeader
title="Boilerplate App"
standalone={standalone}
colorMode="light"
/>
);
}
Customization:
- Change
titleto your app name - Adjust
colorModefor theme consistency
Sidebar Component
Navigation sidebar with configurable links:
export function Sidebar({ standalone = false }: SidebarProps) {
const links: SidebarLink[] = [
{ path: '/home', label: 'Home', icon: 'home', priority: 10 },
// Add your navigation links here
...(standalone ? [
{ path: '/users', label: 'Users', icon: 'users', priority: 70 },
// Additional standalone-only links
] : []),
];
return (
<SharedSidebar
title="Boilerplate App"
links={links}
standalone={standalone}
colorMode="dark"
isActive={isActive}
/>
);
}
Customization:
- Add navigation links with
path,label,icon,priority - Use conditional spreading for mode-specific links
prioritydetermines link ordering (lower = higher position)
flowstate.json - App Manifest
Declares app metadata and contributions:
{
"id": "boilerplate",
"displayName": "Boilerplate",
"version": "1.0.0",
"description": "Template app for creating new FlowState applications",
"icon": "box",
"color": "#64748B",
"route": "/apps/boilerplate",
"category": "other",
"contributes": {
"commands": [
{
"id": "boilerplate.example",
"title": "Example Command",
"category": "Boilerplate"
}
],
"menus": [],
"keybindings": []
},
"activationEvents": ["onRoute:/boilerplate"],
"permissions": []
}
Contribution types:
commands- Registered commands with IDs and titlesmenus- Menu contributions for header/context menuskeybindings- Keyboard shortcuts
Configuration Files
vite.config.mts
Uses shared Vite configuration from the framework:
import { createViteConfig } from '@epicdm/flowstate-app-framework/config/vite';
export default createViteConfig({
port: 3210,
});
Choose a unique port (3200-3299 range is available for apps).
tsup.config.ts
Build configuration for library output:
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
external: [
'react',
'react-dom',
'react-router-dom',
'@epicdm/flowstate-app-framework',
'lucide-react',
],
});
Mark peer dependencies as external to avoid bundling them.
Customization Workflow
Step 1: Copy and Rename
cp -r packages/apps/flowstate-app-boilerplate packages/apps/flowstate-app-myapp
cd packages/apps/flowstate-app-myapp
Step 2: Update Package Identity
Edit package.json:
{
"name": "@epicdm/flowstate-app-myapp",
"description": "My custom FlowState application"
}
Step 3: Update App Manifest
Edit flowstate.json:
{
"id": "myapp",
"displayName": "My App",
"route": "/apps/myapp",
"icon": "your-icon",
"color": "#your-color"
}
Step 4: Update Plugin Registration
Edit src/plugin.ts:
export const myappPlugin: AppPlugin = {
id: 'myapp',
name: 'My App',
route: '/apps/myapp',
config: {
basePath: '/myapp',
},
// ...
};
Step 5: Update Components
- Rename
Header.tsxtitle - Update
Sidebar.tsxnavigation links - Replace
HomePage.tsxcontent - Add new pages and routes
Step 6: Register in Host
Add your plugin to the host container's plugin list:
import { myappPlugin } from '@epicdm/flowstate-app-myapp';
const plugins = [
// ... other plugins
myappPlugin,
];
Best Practices
Naming Conventions
| Item | Pattern | Example |
|---|---|---|
| Package name | @epicdm/flowstate-app-{name} | @epicdm/flowstate-app-inventory |
| Plugin export | {name}Plugin | inventoryPlugin |
| App ID | lowercase | inventory |
| Route | /apps/{name} | /apps/inventory |
| Dev port | 3200-3299 | 3215 |
Component Organization
- Place page components in
src/pages/ - Place reusable components in
src/components/ - Group related components in subdirectories (e.g.,
src/components/forms/) - Use hooks for data fetching and state (e.g.,
src/hooks/)
Standalone Mode Usage
Standalone mode is intended for:
- Development and testing
- Debugging in isolation
- Demonstrating app functionality
Standalone-specific features (like Users/Orgs pages) should be conditionally rendered.
Plugin Best Practices
- Use lazy loading for the main component via dynamic import
- Keep
onLoadinitialization minimal - Clean up resources in
onUnload - Use the services API for accessing framework features
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Routes not working | Check basePath matches between plugin and App |
| Styles not loading | Ensure CSS imports in index.ts and standalone.tsx |
| Build errors | Check external dependencies in tsup.config.ts |
| HMR not working | Verify Vite config and port availability |
Debugging Tips
- Check browser console for plugin lifecycle messages
- Use React DevTools to inspect component tree
- Verify routing with React Router DevTools
- Check RxDB collections in browser dev tools (Application tab)