# Complex Application Stack ## Overview The complex application stack is designed for sophisticated multi-view applications requiring advanced state management, complex navigation, and robust architecture. Use for SaaS platforms, enterprise tools, and feature-rich applications. ## When to Use - SaaS platforms - Enterprise applications - Multi-user systems - Complex workflows with many views - Applications requiring centralized state - Tools with advanced features and interactions ## Extends Default Stack This stack includes ALL packages from `default-webapp.md` PLUS the following additive packages. ## Additional Packages ### State Management ```bash pnpm add zustand # Lightweight, flexible state management ``` ### Date & Time Handling ```bash pnpm add date-fns ``` ### Additional Utilities ```bash pnpm add uuid # Generate unique IDs pnpm add react-resizable-panels # Resizable layouts (often in shadcn) ``` ### Additional shadcn Components ```bash # Navigation & Layout pnpm dlx shadcn@latest add navigation-menu pnpm dlx shadcn@latest add breadcrumb pnpm dlx shadcn@latest add sidebar pnpm dlx shadcn@latest add resizable pnpm dlx shadcn@latest add sheet # Data & Forms pnpm dlx shadcn@latest add table pnpm dlx shadcn@latest add select pnpm dlx shadcn@latest add combobox pnpm dlx shadcn@latest add command pnpm dlx shadcn@latest add calendar pnpm dlx shadcn@latest add popover pnpm dlx shadcn@latest add dropdown-menu # Feedback pnpm dlx shadcn@latest add alert pnpm dlx shadcn@latest add alert-dialog pnpm dlx shadcn@latest add toast pnpm dlx shadcn@latest add progress pnpm dlx shadcn@latest add skeleton ``` ## Architecture Patterns ### State Management with Zustand Create stores for different domains: ```tsx // src/stores/useAuthStore.ts import { create } from 'zustand'; import { persist } from 'zustand/middleware'; interface User { id: string; name: string; email: string; } interface AuthState { user: User | null; token: string | null; login: (user: User, token: string) => void; logout: () => void; } export const useAuthStore = create()( persist( (set) => ({ user: null, token: null, login: (user, token) => set({ user, token }), logout: () => set({ user: null, token: null }), }), { name: 'auth-storage', } ) ); ``` ```tsx // src/stores/useAppStore.ts import { create } from 'zustand'; interface AppState { sidebarOpen: boolean; currentView: string; toggleSidebar: () => void; setView: (view: string) => void; } export const useAppStore = create((set) => ({ sidebarOpen: true, currentView: 'dashboard', toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), setView: (view) => set({ currentView: view }), })); ``` ### Application Layout ```tsx // src/components/AppLayout.tsx import { Outlet } from '@tanstack/react-router'; import { Sidebar } from './Sidebar'; import { Header } from './Header'; import { useAppStore } from '@/stores/useAppStore'; export function AppLayout() { const sidebarOpen = useAppStore((state) => state.sidebarOpen); return (
{/* Sidebar */} {sidebarOpen && ( )} {/* Main content */}
); } ``` ## Layout Scrolling Patterns Complex applications often need independent scrolling areas (sidebar, main content, modals). Getting scrolling right requires understanding flexbox constraints and when to use ScrollArea vs native overflow. ### Core Principle: Flexbox Container Pattern For scrolling to work in constrained spaces, follow this pattern: ```tsx
{/* Define height constraint */}
Fixed header
Scrollable content
Fixed footer
``` **Key rules:** - Parent must have explicit height (`h-screen`, `h-full`, or `flex-1` from its parent) - Fixed elements use `flex-shrink-0` - Scrollable element uses `flex-1` to fill remaining space - Scrollable element needs `overflow-auto` or be a `ScrollArea` component ### Split-Pane Layout (Sidebar + Content) Most complex apps use this pattern: ```tsx // Full-height split pane with independent scrolling export function AppShell() { return (
{/* Left Sidebar */}

Sidebar Header

{/* Right Content */}

Page Header

{/* Main content that scrolls */}
); } ``` **Why this works:** - Root uses `h-screen overflow-hidden` to constrain total height - Both panes use `flex flex-col` to stack header + scrollable area - Headers use `flex-shrink-0` to prevent compression - Scrollable areas use `flex-1 overflow-auto` to fill remaining space ### When to Use ScrollArea vs Native Overflow **Use native `overflow-auto` (recommended):** - Full-height scrolling areas (sidebars, main content) - Simpler implementation, more reliable - Better browser support and performance - Mobile-friendly (respects system scroll behavior) - Works consistently with flexbox layouts **Use shadcn `ScrollArea` (advanced):** - Small contained regions with custom scrollbar styling - Modals/dialogs with fixed max-height - When you need precise scrollbar customization - Note: Can have edge cases with flex-1, prefer native overflow for full-height ```tsx {/* Full-height areas - use native overflow (recommended) */}
Sidebar or main content
{/* Small contained areas - ScrollArea acceptable */}
Fixed height content
``` ### Common Mistakes to Avoid ❌ **Don't use calc() for scroll heights:** ```tsx // BAD: Brittle, breaks on content changes ``` ✅ **Do use flexbox:** ```tsx // GOOD: Flexible, adapts to any layout
``` ❌ **Don't forget parent constraints:** ```tsx // BAD: flex-1 without parent height does nothing
``` ✅ **Do establish height on parent:** ```tsx // GOOD: Parent has h-screen
``` ❌ **Don't skip overflow-hidden on parent:** ```tsx // BAD: Can cause layout issues
``` ✅ **Do add overflow-hidden to parent:** ```tsx // GOOD: Prevents flex-1 from growing beyond bounds
``` ### Modal with Scrollable Content ```tsx Modal Title
{/* Long scrollable content */}
``` ### Debugging Scrolling Issues If scrolling doesn't work: 1. **Check parent height:** Use browser devtools to verify the scrollable element's parent has a constrained height 2. **Verify flex setup:** Ensure `flex flex-col` on parent, `flex-1` on scrollable child 3. **Confirm overflow:** Check if `overflow-auto` or ScrollArea is applied 4. **Look for overflow-hidden:** Parent of scrollable might need `overflow-hidden` to constrain growth **Quick diagnostic:** ```tsx // Add temporary red border to see element bounds
``` If the red border doesn't have the height you expect, the problem is in the parent flex setup, not the scrolling itself. ### Protected Routes ```tsx // src/routes/_authenticated.tsx import { createFileRoute, redirect } from '@tanstack/react-router'; import { useAuthStore } from '@/stores/useAuthStore'; export const Route = createFileRoute('/_authenticated')({ beforeLoad: async () => { const token = useAuthStore.getState().token; if (!token) { throw redirect({ to: '/login' }); } }, component: AppLayout, }); ``` ```tsx // src/routes/_authenticated/dashboard.tsx export const Route = createFileRoute('/_authenticated/dashboard')({ component: Dashboard, }); ``` ## Navigation Patterns ### Sidebar Navigation ```tsx import { Link } from '@tanstack/react-router'; import { Home, Users, Settings, FileText } from 'lucide-react'; import { cn } from '@/lib/utils'; const navigation = [ { name: 'Dashboard', href: '/dashboard', icon: Home }, { name: 'Users', href: '/users', icon: Users }, { name: 'Documents', href: '/documents', icon: FileText }, { name: 'Settings', href: '/settings', icon: Settings }, ]; export function Sidebar() { return ( ); } ``` ### Hierarchical Sidebar Items When displaying hierarchical data (org charts, nested navigation, file trees) in sidebars, use padding-based indentation to prevent text clipping. **✅ Correct Pattern:** ```tsx interface HierarchyItemProps { person: { name: string; title: string; initials: string }; level: number; // 0, 1, 2 for depth isSelected: boolean; } function HierarchyItem({ person, level, isSelected }: HierarchyItemProps) { const paddingLeft = level === 0 ? 'pl-3' : level === 1 ? 'pl-5' : 'pl-7'; return ( ); } ``` **Key Rules:** ❌ **Don't use margin for indentation:** ```tsx Page {page}
); } ``` ## Complex Forms ### Multi-Step Form ```tsx import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; const step1Schema = z.object({ name: z.string().min(2), email: z.string().email(), }); const step2Schema = z.object({ company: z.string().min(2), role: z.string(), }); export function MultiStepForm() { const [step, setStep] = useState(1); const [formData, setFormData] = useState({}); const schema = step === 1 ? step1Schema : step2Schema; const form = useForm({ resolver: zodResolver(schema), }); const onSubmit = (data: any) => { if (step === 1) { setFormData({ ...formData, ...data }); setStep(2); } else { // Final submission const finalData = { ...formData, ...data }; console.log(finalData); } }; const progress = (step / 2) * 100; return (
{step === 1 && ( <> {/* Step 1 fields */} )} {step === 2 && ( <> {/* Step 2 fields */} )}
{step > 1 && ( )}
); } ``` ## Performance Optimization ### Code Splitting by Route TanStack Router handles this automatically. Each route is lazy-loaded. ### Virtual Scrolling for Large Lists ```tsx import { useVirtualizer } from '@tanstack/react-virtual'; import { useRef } from 'react'; export function VirtualList({ items }: { items: any[] }) { const parentRef = useRef(null); const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, }); return (
{virtualizer.getVirtualItems().map((virtualItem) => (
{items[virtualItem.index].name}
))}
); } ``` ### React Compiler Enable for automatic memoization: ```bash pnpm add -D babel-plugin-react-compiler ``` Configure in Vite: ```typescript // vite.config.ts export default defineConfig({ plugins: [ react({ babel: { plugins: [ ['babel-plugin-react-compiler', {}], ], }, }), ], }); ``` ## Testing Strategy ### Component Testing ```tsx import { render, screen } from '@testing-library/react'; import { Button } from './button'; test('renders button with text', () => { render(); expect(screen.getByText('Click me')).toBeInTheDocument(); }); ``` ### E2E Testing (Playwright) ```bash pnpm add -D @playwright/test ``` ```typescript // tests/login.spec.ts import { test, expect } from '@playwright/test'; test('user can log in', async ({ page }) => { await page.goto('/login'); await page.fill('[name="email"]', 'user@example.com'); await page.fill('[name="password"]', 'password'); await page.click('button[type="submit"]'); await expect(page).toHaveURL('/dashboard'); }); ``` ## Security Considerations - Validate all inputs with Zod - Sanitize user-generated content - Use HTTPS in production - Implement CSRF protection - Set secure cookie flags - Use environment variables for secrets - Implement rate limiting - Regular dependency updates ## Deployment ### Environment Variables ```bash # .env.production VITE_API_URL=https://api.production.com VITE_APP_URL=https://app.production.com ``` Access in code: ```typescript const API_URL = import.meta.env.VITE_API_URL; ``` ### Build Optimization ```bash # Production build pnpm build # Analyze bundle size pnpm add -D rollup-plugin-visualizer ``` ## Next Steps 1. Define application architecture and feature domains 2. Set up Zustand stores for global state 3. Create protected route structure 4. Implement authentication flow 5. Build core navigation (sidebar, command palette) 6. Develop feature modules 7. Add comprehensive error handling 8. Optimize performance (code splitting, caching) 9. Implement testing strategy 10. Deploy to production