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 FlowstateAppContainer for framework context
  • Uses FlowstateAppLayout for consistent layout structure
  • Conditionally wraps with BrowserRouter based on useHostRouter config
  • Passes standalone prop 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 identity
  • icon, color - Visual appearance in launcher
  • route - URL path in host application
  • category - Grouping in app launcher
  • config.basePath - Base path for internal routing
  • onLoad/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:

  1. ServerConfigPlugin - Server connection configuration
  2. DatabasePlugin - RxDB database setup (memory adapter for dev)
  3. OrgWorkspacePlugin - Organization/workspace context
  4. AuthPlugin - 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 title to your app name
  • Adjust colorMode for theme consistency

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
  • priority determines 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 titles
  • menus - Menu contributions for header/context menus
  • keybindings - 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

  1. Rename Header.tsx title
  2. Update Sidebar.tsx navigation links
  3. Replace HomePage.tsx content
  4. 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

ItemPatternExample
Package name@epicdm/flowstate-app-{name}@epicdm/flowstate-app-inventory
Plugin export{name}PlugininventoryPlugin
App IDlowercaseinventory
Route/apps/{name}/apps/inventory
Dev port3200-32993215

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 onLoad initialization minimal
  • Clean up resources in onUnload
  • Use the services API for accessing framework features

Troubleshooting

Common Issues

IssueSolution
Routes not workingCheck basePath matches between plugin and App
Styles not loadingEnsure CSS imports in index.ts and standalone.tsx
Build errorsCheck external dependencies in tsup.config.ts
HMR not workingVerify Vite config and port availability

Debugging Tips

  1. Check browser console for plugin lifecycle messages
  2. Use React DevTools to inspect component tree
  3. Verify routing with React Router DevTools
  4. Check RxDB collections in browser dev tools (Application tab)
Previous
Getting Started