- Learn
- AI Code Editors
- Windsurf
- Flows and Automation
Intermediate15 min1 prerequisite
Use Windsurf Flows to automate repetitive coding tasks, create custom workflows, and boost productivity.
Flows and Automation
Flows are Windsurf's automation system for repetitive coding tasks. Define a workflow once, then execute it with a single command.
What are Flows?
Flows automate multi-step coding tasks:
Terminal
Without Flows:
1. Create component file
2. Add boilerplate code
3. Create test file
4. Add test boilerplate
5. Create styles file
6. Update exports
With Flow "Create React Component":
1. Run flow
2. Enter component name
3. All files created instantly
Built-in Flows
Accessing Flows
Open the Flows panel:
Terminal
Cmd/Ctrl+Shift+P → "Flows: Open Panel"
Or click the Flows icon in the activity bar.
Available Built-in Flows
| Flow | Description |
|---|---|
| Create Component | React/Vue/Angular component with tests |
| Create API Route | Next.js/Express API endpoint |
| Create Hook | Custom React hook |
| Create Service | Service class with methods |
| Create Test | Test file for selected code |
| Scaffold Feature | Full feature with all parts |
Running a Flow
Terminal
1. Open Flows panel
2. Select flow (e.g., "Create React Component")
3. Enter parameters:
- Component name: UserProfile
- Include tests: Yes
- Include styles: CSS Modules
4. Click "Run"
5. Files are created
Flow Examples
Create React Component
Flow Definition:
Terminal
name: Create React Component
inputs:
- name: componentName
type: string
prompt: "Component name"
- name: includeTests
type: boolean
default: true
- name: styling
type: select
options: ["none", "css", "css-modules", "styled-components"]
outputs:
- path: components/{{componentName}}/{{componentName}}.tsx
- path: components/{{componentName}}/{{componentName}}.test.tsx
condition: includeTests
- path: components/{{componentName}}/{{componentName}}.module.css
condition: styling == "css-modules"
Result:
Terminal
components/
└── UserProfile/
├── UserProfile.tsx
├── UserProfile.test.tsx
└── UserProfile.module.css
Create API Route
Input:
Terminal
Route name: users
Methods: GET, POST
Validation: Yes (Zod)
Output:
Terminal
// app/api/users/route.ts
import { NextResponse } from 'next/server'
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
export async function GET() {
const users = await prisma.user.findMany()
return NextResponse.json(users)
}
export async function POST(request: Request) {
const body = await request.json()
const validated = userSchema.parse(body)
const user = await prisma.user.create({ data: validated })
return NextResponse.json(user, { status: 201 })
}
Scaffold Feature
Input:
Terminal
Feature name: subscription
Include: model, service, api, ui, tests
Output:
Terminal
src/
├── prisma/
│ └── migrations/add_subscription/
├── services/
│ └── subscription.service.ts
├── app/api/subscriptions/
│ ├── route.ts
│ └── [id]/route.ts
├── components/
│ └── Subscription/
│ ├── SubscriptionCard.tsx
│ └── SubscriptionForm.tsx
├── types/
│ └── subscription.ts
└── tests/
└── subscription/
├── subscription.service.test.ts
└── subscription.api.test.ts
Creating Custom Flows
Flow File Location
Create flows in .windsurf/flows/:
Terminal
.windsurf/
└── flows/
├── create-feature.yaml
├── add-endpoint.yaml
└── generate-types.yaml
Flow Structure
Terminal
# .windsurf/flows/create-feature.yaml
name: Create Feature
description: Scaffold a complete feature with all components
version: 1.0.0
inputs:
- name: featureName
type: string
prompt: "Feature name (e.g., 'subscription')"
validate: "^[a-z][a-z0-9-]*$"
- name: includeAPI
type: boolean
prompt: "Include API routes?"
default: true
- name: includeUI
type: boolean
prompt: "Include UI components?"
default: true
steps:
- action: create_file
path: "services/{{featureName}}.service.ts"
template: service.template.ts
- action: create_file
path: "types/{{featureName}}.ts"
template: types.template.ts
- action: create_file
path: "app/api/{{featureName}}/route.ts"
template: api-route.template.ts
condition: "{{includeAPI}}"
- action: create_directory
path: "components/{{featureName | pascalCase}}"
condition: "{{includeUI}}"
- action: create_file
path: "components/{{featureName | pascalCase}}/{{featureName | pascalCase}}.tsx"
template: component.template.tsx
condition: "{{includeUI}}"
- action: run_command
command: "npm run lint:fix"
Templates
Create templates in .windsurf/templates/:
Terminal
// .windsurf/templates/service.template.ts
import { prisma } from '@/lib/prisma'
import type { {{featureName | pascalCase}} } from '@/types/{{featureName}}'
export class {{featureName | pascalCase}}Service {
async findAll(): Promise<{{featureName | pascalCase}}[]> {
return prisma.{{featureName}}.findMany()
}
async findById(id: string): Promise<{{featureName | pascalCase}} | null> {
return prisma.{{featureName}}.findUnique({ where: { id } })
}
async create(data: Omit<{{featureName | pascalCase}}, 'id'>): Promise<{{featureName | pascalCase}}> {
return prisma.{{featureName}}.create({ data })
}
async update(id: string, data: Partial<{{featureName | pascalCase}}>): Promise<{{featureName | pascalCase}}> {
return prisma.{{featureName}}.update({ where: { id }, data })
}
async delete(id: string): Promise<void> {
await prisma.{{featureName}}.delete({ where: { id } })
}
}
Template Helpers
Available template helpers:
| Helper | Example Input | Output |
|---|---|---|
pascalCase | user-profile | UserProfile |
camelCase | user-profile | userProfile |
snakeCase | userProfile | user_profile |
kebabCase | UserProfile | user-profile |
plural | user | users |
singular | users | user |
Flow Actions
create_file
Terminal
- action: create_file
path: "path/to/file.ts"
template: template-file.ts
overwrite: false # Don't overwrite if exists
create_directory
Terminal
- action: create_directory
path: "path/to/directory"
run_command
Terminal
- action: run_command
command: "npm install some-package"
cwd: "{{workspaceRoot}}"
modify_file
Terminal
- action: modify_file
path: "package.json"
operation: json_merge
data:
scripts:
"{{featureName}}:dev": "..."
cascade_prompt
Invoke Cascade AI:
Terminal
- action: cascade_prompt
prompt: |
Create a README for the {{featureName}} feature.
Include usage examples and API documentation.
output: "docs/{{featureName}}.md"
Conditional Logic
Simple Conditions
Terminal
- action: create_file
path: "tests/{{name}}.test.ts"
condition: "{{includeTests}}"
Complex Conditions
Terminal
- action: create_file
path: "{{name}}.styles.ts"
condition: "{{styling == 'styled-components'}}"
- action: create_file
path: "{{name}}.module.css"
condition: "{{styling == 'css-modules'}}"
Sharing Flows
Team Flows
Share flows via the repository:
Terminal
.windsurf/
└── flows/
└── team-flows/
├── create-feature.yaml
├── add-migration.yaml
└── README.md
Commit to version control for team access.
Publishing Flows
Export flows for others:
Terminal
Flows Panel → Your Flow → Export
Share the exported .flow file.
Importing Flows
Terminal
Flows Panel → Import → Select .flow file
Best Practices
1. Start Simple
Begin with basic flows, then add complexity:
Terminal
# v1: Simple
steps:
- action: create_file
path: "components/{{name}}.tsx"
template: component.tsx
# v2: Add options
inputs:
- name: includeTests
type: boolean
default: true
# v3: Add conditional steps
# v4: Add post-processing
2. Use Descriptive Names
Terminal
# ❌
name: cf
# ✅
name: Create React Feature Component
description: Creates a feature component with tests, styles, and stories
3. Validate Inputs
Terminal
inputs:
- name: componentName
validate: "^[A-Z][a-zA-Z0-9]*$"
errorMessage: "Component name must be PascalCase"
4. Document Flows
Terminal
name: Create API Endpoint
description: |
Creates a complete API endpoint with:
- Route handler
- Input validation
- Error handling
- TypeScript types
Usage: Run this flow and provide the endpoint name.
Troubleshooting
Flow Not Appearing
Terminal
1. Check file location (.windsurf/flows/)
2. Verify YAML syntax
3. Reload Windsurf
Template Errors
Terminal
1. Check template path
2. Verify variable names match inputs
3. Test helpers (pascalCase, etc.)
Conditional Not Working
Terminal
1. Check boolean vs string comparison
2. Use correct syntax: {{variable == 'value'}}
3. Ensure input names match
Summary
- Flows automate repetitive coding tasks
- Built-in flows for common operations
- Custom flows in
.windsurf/flows/ - Templates with variable substitution
- Actions: create_file, run_command, modify_file
- Conditions for flexible workflows
- Share flows with team via repository
What's Next
You've completed the Windsurf module. You can now:
- Use Cascade for AI development
- Create Flows for automation
- Combine both for maximum productivity
Consider exploring:
- Cursor for another agentic editor experience
- Claude Code for terminal-based AI development
- GitHub Copilot for multi-editor support
Mark this lesson as complete to track your progress