Frontend Development.mdc
Guidelines for frontend development in Langflow, focusing on React/TypeScript UI components, build processes, and frontend testing.
Loading actions...
Skill content
Main instructions and any bundled files for this skill.
1. Frontend Environment Setup
Prerequisites
- Node.js: v22.12 LTS for JavaScript runtime
- Package Manager: npm (v10.9) for dependency management
- Development Tools: Vite for build tooling
Frontend Service
make frontend # Start Vite dev server on port 3000
- Hot-reload enabled for UI changes
- Access at: http://localhost:3000/
- Frontend source:
src/frontend/
2. Frontend Structure
Directory Layout
src/frontend/src/
├── components/ # Reusable UI components
├── pages/ # Page-level components
├── icons/ # Component icons and lazy loading
├── stores/ # State management (Zustand)
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── hooks/ # Custom React hooks
├── services/ # API service functions
└── assets/ # Static assets
Key Technologies
- React 18 with TypeScript
- Vite for build tooling and dev server
- Tailwind CSS for styling
- Zustand for state management
- React Flow for flow graph visualization
- Lucide React for icons
3. Frontend Code Quality
Formatting
make format_frontend # Format TypeScript/JavaScript code
Linting
make lint # Run ESLint and TypeScript checks
Testing
make tests_frontend # Run frontend tests (requires additional setup)
Pre-commit Workflow
- Run
make format_frontend - Run
make lint - Test changes in browser
- Commit changes
4. State Management
Zustand Stores
// stores/myStore.ts
import { create } from 'zustand';
interface MyState {
value: string;
setValue: (value: string) => void;
}
export const useMyStore = create<MyState>((set) => ({
value: '',
setValue: (value) => set({ value }),
}));
Using Stores in Components
// components/MyComponent.tsx
import { useMyStore } from '@/stores/myStore';
export function MyComponent() {
const { value, setValue } = useMyStore();
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
5. API Integration
Service Functions
// services/api.ts
import { api } from '@/controllers/API';
export async function createFlow(flowData: FlowData) {
const response = await api.post('/flows/', flowData);
return response.data;
}
export async function getFlows() {
const response = await api.get('/flows/');
return response.data;
}
Error Handling
// hooks/useApi.ts
import { useState, useCallback } from 'react';
export function useApi<T>(apiFunction: (...args: any[]) => Promise<T>) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const execute = useCallback(async (...args: any[]) => {
try {
setLoading(true);
setError(null);
const result = await apiFunction(...args);
return result;
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
throw err;
} finally {
setLoading(false);
}
}, [apiFunction]);
return { execute, loading, error };
}
6. React Flow Integration
Flow Graph Components
// components/FlowGraph.tsx
import ReactFlow, {
Node,
Edge,
Controls,
Background
} from 'reactflow';
interface FlowGraphProps {
nodes: Node[];
edges: Edge[];
onNodesChange: (changes: NodeChange[]) => void;
onEdgesChange: (changes: EdgeChange[]) => void;
}
export function FlowGraph({
nodes,
edges,
onNodesChange,
onEdgesChange
}: FlowGraphProps) {
return (
<div className="w-full h-full">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
fitView
>
<Controls />
<Background />
</ReactFlow>
</div>
);
}
Custom Node Types
// components/nodes/ComponentNode.tsx
import { memo } from 'react';
import { Handle, Position } from 'reactflow';
interface ComponentNodeProps {
data: {
label: string;
icon?: string;
};
}
export const ComponentNode = memo(({ data }: ComponentNodeProps) => {
return (
<div className="px-4 py-2 shadow-md rounded-md bg-white border">
<Handle type="target" position={Position.Top} />
<div className="flex items-center">
{data.icon && (
<img src={data.icon} alt="" className="w-4 h-4 mr-2" />
)}
<span className="text-sm">{data.label}</span>
</div>
<Handle type="source" position={Position.Bottom} />
</div>
);
});
7. Styling with Tailwind
Component Styling
// components/Button.tsx
import { cn } from '@/utils/cn';
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
}
export function Button({
variant = 'primary',
size = 'md',
children,
onClick
}: ButtonProps) {
return (
<button
className={cn(
'rounded-md font-medium transition-colors',
{
'bg-blue-600 hover:bg-blue-700 text-white': variant === 'primary',
'bg-gray-200 hover:bg-gray-300 text-gray-900': variant === 'secondary',
'px-2 py-1 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
}
)}
onClick={onClick}
>
{children}
</button>
);
}
Dark Mode Support
// hooks/useDarkMode.ts
import { useDarkStore } from '@/stores/darkStore';
export function useDarkMode() {
const { dark, setDark } = useDarkStore();
const toggle = () => setDark(!dark);
return { isDark: dark, toggle };
}
8. Frontend Testing
Component Testing
// __tests__/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from '@/components/Button';
describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Integration Testing
// __tests__/FlowEditor.test.tsx
import { render, screen } from '@testing-library/react';
import { FlowEditor } from '@/pages/FlowEditor';
describe('FlowEditor', () => {
it('loads flow data correctly', async () => {
render(<FlowEditor flowId="test-flow-id" />);
// Wait for flow to load
await screen.findByText('Flow loaded');
expect(screen.getByTestId('flow-canvas')).toBeInTheDocument();
});
});
9. Build and Deployment
Development Build
make build_frontend # Build frontend static files
Production Build
cd src/frontend
npm run build # Creates dist/ directory
Build Integration
- Frontend builds to
src/frontend/dist/ - Backend serves static files from this directory in production
make initincludes frontend build step
Frontend Development Checklist
-
Frontend service running with
make frontend -
Changes hot-reload correctly in browser
-
State management uses Zustand stores
-
API calls use proper error handling
-
Components styled with Tailwind CSS
-
Dark mode support implemented where needed
-
Code formatted with
make format_frontend -
Linting passed with
make lint -
Changes tested in both light and dark mode
-
Components styled with Tailwind CSS
-
Dark mode support implemented where needed
-
Code formatted with
make format_frontend -
Changes tested in both light and dark mode
Related Skills
Frontend Typescript Linting.mdc
TypeScript and ESLint rules that MUST be followed when creating, modifying, or reviewing any file under apps/frontend/, including .ts, .tsx, .js, and .jsx files. Also apply when discussing frontend li...
2. Apply Deepthink Protocol (reason about dependencies
risks