How to Build a Complete Frontend Project with Windsurf
This tutorial builds a real React and TypeScript task dashboard with Windsurf. It covers requirements, architecture, scaffolding, state, validation, accessibility, testing, production builds, and deployment, with reusable prompts and measured project results.
One of the most misleading experiences in AI-assisted development is this:
You ask for a website, see a page a few minutes later, and assume the project is finished.A complete frontend project still needs decisions and verification around:
- Technology choices;
- Maintainable structure;
- State and data flow;
- Form validation;
- Responsive behavior;
- Keyboard and screen-reader access;
- Type safety and linting;
- Automated tests;
- Production builds;
- Secrets and environment variables;
- Deployment and future maintenance.
Windsurf is most valuable when it operates across the editor, repository, and terminal: reading the current codebase, making a plan, editing several files, running commands, interpreting failures, and applying a targeted fix.
Naming note: After Cognition acquired Windsurf, the 2026 documentation increasingly uses the name âDevin Desktop.â This article keeps the better-known Windsurf name and focuses on the Cascade workflow. Labels may differ slightly between versions.
1. What are we building?
The example project is TaskFlow, a responsive task-management dashboard.
It includes:
- Dashboard;
- Task list;
- Validated task form;
- Task status and priority;
- Lightweight analytics;
- Dark mode;
- Local persistence;
- Desktop, tablet, and mobile layouts;
- Unit tests;
- Playwright end-to-end test scripts;
- Production build and deployment configuration.
Technology stack
| Area | Choice |
|---|---|
| Framework | React 19 |
| Language | TypeScript |
| Build tool | Vite 8 |
| Routing | React Router |
| State | Zustand |
| Forms | React Hook Form |
| Validation | Zod |
| Icons | Lucide React |
| Unit tests | Vitest + Testing Library |
| E2E | Playwright |
| Styling | CSS custom properties and responsive CSS |
This is not the only valid stack. It is a compact modern setup that demonstrates a maintainable application without hiding the implementation behind a large component framework.
2. Reproducible test results
The tutorial project was actually created, checked, tested, and built.
Test environment
| Item | Version or condition |
|---|---|
| Node.js | 22.16.0 |
| npm | 10.9.2 |
| React | 19.2.7 |
| TypeScript | 6.0.3 |
| Vite | 8.1.0 |
| Vitest | 4.1.9 |
| Operating system | Linux container |
Results
| Check | Measured result |
|---|---|
| Source files | 21 |
| Vite modules transformed | 138 |
| Oxlint | 0 warnings, 0 errors |
| TypeScript | 0 errors |
| Unit tests | 4/4 passed |
| Vite production-build phase | about 0.75 seconds |
| Full `npm run build` command | about 3.4 seconds |
| Main JavaScript | 337.51 KB |
| Main JavaScript gzip | 105.79 KB |
| CSS | 7.48 KB |
| CSS gzip | 2.26 KB |
| `dist` directory | about 353 KB |
| Production dependency audit | 0 known vulnerabilities |
These measurements describe the example project, not âhow fast Windsurf codes.â Hardware, dependency versions, and project scope will change the results.
Three Playwright flows were also written:
- Create and complete a task;
- Persist dark mode after reload;
- Use mobile navigation.
The managed Chromium environment used for this article blocked local-page access, so those scripts are not misrepresented as executed passes. On a normal development machine, install the Playwright browsers and run:
```bash
npx playwright install
npm run test:e2e
```
3. Install Windsurf and prepare the machine
Windsurf currently supports macOS, Windows, and Linux and can import settings from VS Code or Cursor.
Install:
- A compatible Node.js version;
- Git;
- Windsurf;
- Chrome or Chromium;
- A GitHub account.
Vite 8 requires Node.js 20.19+ or 22.12+.
Verify the environment:
```bash
node -v
npm -v
git --version
```
Is the free plan enough?
Public plans in June 2026 include:
| Plan | Public price |
|---|---|
| Free | $0/month |
| Pro | $20/month |
| Max | $200/month |
| Team | Starts around $80/month, with seat structure shown at checkout |
The free plan is sufficient for this tutorial, although agent quotas and model choices are more limited. Pro is more practical for sustained work and larger repositories.
Pricing and quotas change frequently. Confirm them on the current checkout page.
4. Write requirements before code
Do not start with:
Build me a task management website.
That prompt forces the agent to guess the framework, pages, data model, quality standards, and visual direction.
Create:
```text
docs/requirements.md
```
Example:
```markdown
TaskFlow requirements
Goal
Build a responsive personal task dashboard.
Pages
- Dashboard
- Tasks
- Analytics
- Settings
- 404
Core features
- Create and delete tasks
- Change task status
- Priority and due date
- Persist data locally
- Dark mode
- Mobile navigation
Quality requirements
- TypeScript strict mode
- Accessible labels and keyboard focus
- Form validation
- Unit tests
- End-to-end test scripts
- Production build must pass
Non-goals
- No backend
- No authentication
- No external database
- No large UI component library
```
Requirements in the repository are more reliable than requirements buried in a chat history.
5. Constrain Windsurf with AGENTS.md
Create this file at the repository root:
```text
AGENTS.md
```
Example:
```markdown
Project instructions
- Package manager: npm.
- Framework: React + TypeScript + Vite.
- Use functional components.
- State management: Zustand.
- Forms: React Hook Form + Zod.
- Unit tests: Vitest + Testing Library.
- E2E tests: Playwright.
- All interactive controls require accessible names.
- Do not add a dependency when a small local implementation is enough.
- Run npm run check before marking a task complete.
- Never place secrets in frontend source code.
```
Current Windsurf documentation recommends:
- `AGENTS.md` for simple directory-scoped instructions;
- Rules for cross-cutting or conditionally activated behavior;
- Skills for repeatable multi-step procedures;
- Explicit rules rather than relying on auto-generated memories for critical conventions.
Without stable instructions, an agent may:
- Switch between npm and pnpm;
- Mix several styling systems;
- Add overlapping dependencies;
- Forget tests;
- Refactor unrelated files while solving a small issue.
6. Ask Cascade for a plan before edits
Start in chat or a non-editing mode:
```text
Read docs/requirements.md and AGENTS.md.
Do not write code yet.
Create an implementation plan that includes:
1. Technology choices and reasons
2. Project directory structure
3. Data model
4. Route design
5. Component boundaries
6. State-management plan
7. Validation strategy
8. Unit and E2E test plan
9. Accessibility checklist
10. Build and deployment checklist
Identify ambiguous requirements and choose the simplest maintainable solution.
```
A useful plan should explain:
- The Task data model;
- Which state is global;
- Page and component boundaries;
- Persistence;
- Critical user flows;
- What is explicitly out of scope.
If the plan is only âcreate pages, add styling, finish the app,â ask for a more concrete plan before allowing edits.
7. Scaffold the Vite project
After approving the plan:
```text
Initialize the project with the latest Vite React TypeScript template.
Use npm.
Do not install application dependencies yet.
After scaffolding:
1. Run npm install
2. Start the development server
3. Report any errors
4. Stop and wait for review
```
Equivalent commands:
```bash
npm create vite@latest taskflow -- --template react-ts
cd taskflow
npm install
npm run dev
```
Vite officially provides a React TypeScript template. There is no reason for the agent to hand-write a custom Webpack setup for this project.
Terminal safety
Windsurfâs terminal provides four auto-execution levels:
- Disabled;
- Allowlist Only;
- Auto;
- Turbo.
Beginners should use Allowlist Only or Auto.
Keep destructive commands behind explicit approval:
```text
rm
sudo
chmod
chown
curl | sh
git push --force
git reset --hard
npm publish
```
An agentâs ability to execute a command is not a reason to approve it automatically.
8. Install only approved dependencies
Prompt:
```text
Install only the dependencies required by the approved architecture:
Runtime:
- react-router-dom
- zustand
- zod
- react-hook-form
- @hookform/resolvers
- lucide-react
Development:
- vitest
- jsdom
- @testing-library/react
- @testing-library/jest-dom
- @testing-library/user-event
- @playwright/test
Before installing:
1. Explain why each package is needed
2. Check whether an existing dependency already solves the problem
3. Use exact package names
4. Do not add a UI framework
```
Commands:
```bash
npm install react-router-dom zustand zod react-hook-form \
@hookform/resolvers lucide-react
npm install -D vitest jsdom \
@testing-library/react \
@testing-library/jest-dom \
@testing-library/user-event \
@playwright/test
```
Uncontrolled agents often add a full chart library for three bars, a date library for one date field, or multiple state solutions.
Every dependency adds bundle weight, security surface, upgrade work, and more code for the agent to understand.
9. Build in stages
Avoid:
Implement the whole plan.
Smaller stages are easier to review and revert.
Stage 1: data model and state
```text
Implement only the data layer.
Requirements:
- Create Task, TaskStatus, and TaskPriority TypeScript types
- Create a Zustand store
- Include addTask, updateStatus, removeTask, and resetTasks
- Persist tasks to localStorage
- Add three seed tasks
- Do not create UI components yet
After editing:
- Run TypeScript type checking
- Explain the public store API
```
Stage 2: shell and routing
```text
Implement the application shell and routing.
Routes:
- /
- /tasks
- /analytics
- /settings
- fallback 404
Requirements:
- Responsive sidebar on desktop
- Bottom navigation on mobile
- Active-route state
- Semantic navigation
- Dark-mode toggle persisted to localStorage
- Do not implement page-specific business logic yet
```
Stage 3: validated form
```text
Implement the task form using React Hook Form and Zod.
Validation:
- title: 3 to 80 characters
- description: maximum 240 characters
- priority: low, medium, or high
- due date: required
Accessibility:
- Every field has a visible label
- Validation errors are readable
- Submit is keyboard accessible
Add focused unit tests for invalid and valid submissions.
```
Stage 4: task list and analytics
```text
Implement:
1. Task list
2. Status selector
3. Delete action
4. Priority labels
5. Dashboard summary cards
6. A dependency-free CSS bar chart for task status
Do not add a chart library.
Preserve the existing store API.
Add tests for status updates and deletion.
```
Stage 5: responsive visual polish
```text
Polish the interface without changing business logic.
Requirements:
- CSS custom properties for light and dark themes
- Desktop, tablet, and 390px mobile layouts
- Visible focus states
- No horizontal overflow
- Touch targets approximately 44px where possible
- Respect semantic heading order
- Avoid low-contrast text
```
10. Review generated changes
Do not judge the result only by whether the page looks polished.
Check the scope
If the task was a form, why did routing, themes, and the store also change?
Look for duplication
Watch for:
- Multiple Task interfaces;
- More than one state system;
- Repeated CSS tokens;
- Duplicate components;
- Unnecessary helpers.
Look for hidden risk
Pay attention to:
- `any`;
- Non-null assertions;
- Empty `catch` blocks;
- Unhandled promises;
- Direct state mutation;
- Incorrect effect dependencies;
- Secrets in client code.
Check real accessibility
Icon-only buttons need accessible names.
Errors cannot be communicated only through red color.
All key interactions must work from the keyboard.
Check maintainability
A 300-line component is not automatically wrong, but the agent should be able to justify the boundary.
11. Create a verification loop
Add scripts:
```json
{
"scripts": {
"dev": "vite",
"lint": "oxlint",
"typecheck": "tsc -b --pretty false",
"test": "vitest run",
"test:e2e": "playwright test",
"build": "tsc -b && vite build",
"check": "npm run lint && npm run typecheck && npm run test && npm run build"
}
}
```
Prompt Cascade:
```text
Run npm run check.
If a command fails:
1. Read the complete error
2. Identify the root cause
3. Make the smallest safe fix
4. Re-run only the failed check first
5. Run npm run check again after it passes
Do not silence TypeScript or lint errors.
Do not remove failing tests unless the requirement is wrong.
```
Measured result:
```text
Lint: 0 warnings, 0 errors
Typecheck: 0 errors
Unit tests: 4 passed
Build: passed
Modules: 138 transformed
JS gzip: 105.79 KB
CSS gzip: 2.26 KB
```
This is the line between AI-generated files and a verified deliverable.
12. Generate useful tests
Do not ask:
Test every component.
That usually creates low-value ârenders without crashingâ tests.
Use behavioral cases:
```text
Add unit tests for business behavior only.
Test cases:
1. Empty title shows a validation error
2. A valid form submission adds one task
3. Changing the status updates the store
4. Deleting a task removes it
Use Testing Library queries based on roles and accessible names.
Do not test implementation details.
```
For Playwright:
```text
Add Playwright tests for three critical flows:
1. Create a task and mark it done
2. Switch to dark mode and verify persistence after reload
3. Use mobile navigation at 390x844
Use stable role- and label-based selectors.
Do not use arbitrary timeouts.
```
Playwright provides a runner, assertions, isolation, parallelization, and mobile emulation for modern browser testing.
13. Measure performance and bundle size
Prompt:
```text
Run a production build and analyze the output.
Report:
- Build time
- Number of transformed modules
- Raw and gzip sizes for JS and CSS
- Any chunk above 200 KB gzip
- Dependencies contributing most to the bundle
- Three optimization opportunities, ranked by impact
Do not optimize before measuring.
```
Measured main JavaScript:
- Raw: 337.51 KB;
- Gzip: 105.79 KB.
That is reasonable for routing, state, form validation, and icons, but there are still options:
- Lazy-load routes;
- Import only required icons;
- Avoid large chart and date packages;
- Split non-critical pages.
Do not hard-code a Lighthouse claim
Lighthouse varies with hardware, browser, network, and run mode.
Run it locally:
```bash
npm run build
npm run preview
npx lighthouse http://localhost:4173 \
--only-categories=performance,accessibility,best-practices,seo \
--view
```
Record the environment and use the median of several runs rather than publishing the single best score.
14. Environment variables and APIs
Create:
```text
.env.example
```
Example:
```bash
VITE_API_BASE_URL=https://api.example.com
```
Read it with:
```ts
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL
```
Every variable prefixed with `VITE_` is exposed to the browser bundle.
Never put these in frontend code:
- OpenAI API keys;
- Database passwords;
- Private keys;
- Admin tokens;
- Payment-provider secrets;
- Service-account credentials.
Use:
```text
Browser -> your backend or serverless function -> third-party API
```
Add a rule:
```text
Never place secrets in frontend code.
If a feature requires a secret, create a server-side boundary or explain why it cannot be implemented safely in a frontend-only project.
```
15. Use Git as the real history
Commit after each stage:
```bash
git add .
git commit -m "feat: add task state and persistence"
```
A useful sequence:
```text
chore: scaffold Vite React TypeScript project
feat: add application shell and routes
feat: add task store and persistence
feat: add validated task form
feat: add task list and analytics
test: add unit and end-to-end coverage
style: add responsive themes
chore: add production checks
```
Cascade can suggest commit messages, but review the diff yourself.
Keep these commands behind manual approval:
```bash
git push --force
git reset --hard
git clean -fd
```
Named checkpoints are useful for reverting agent changes, but Git remains the official history.
16. Deploy
Windsurf App Deploys can deploy React, Vue, Svelte, and Next.js projects to Netlify and return a public preview URL.
Prompt:
```text
Before deployment:
1. Run npm run check
2. Run the production build
3. Verify that no .env files or secrets are included
4. Summarize the build output
If all checks pass, deploy this project to Netlify as a preview.
```
Official caveats:
- App Deploys is still beta;
- Netlify is the currently documented provider;
- Code is uploaded through the Windsurf/Cognition deployment workflow;
- Sensitive production deployments should be claimed in your own account;
- Unclaimed deployments may be removed.
For production:
1. Push to your own GitHub repository;
2. Connect Netlify, Vercel, or Cloudflare Pages;
3. Configure the build and environment variables;
4. Enable branch previews;
5. Keep logs and rollback options.
17. Five high-value prompt patterns
Plan before editing
```text
Do not edit files yet. Inspect the codebase and propose a plan with affected files, risks, and validation steps.
```
Limit scope
```text
Only modify the task-form feature. Do not refactor routing, theme, or the store unless required. Explain every out-of-scope change before making it.
```
Make the agent verify
```text
After implementation, run lint, typecheck, focused tests, and production build. Fix root causes instead of suppressing errors.
```
Ask for the smallest safe change
```text
Make the smallest safe change that satisfies the requirement. Preserve public APIs and existing behavior.
```
Request review instead of more code
```text
Review the current implementation as a senior frontend engineer. List correctness, accessibility, performance, security, and maintainability issues. Do not edit files until I approve the findings.
```
18. Common failure modes
Generating the whole project at once
Symptoms:
- Many files with weak structure;
- Features that appear complete but cannot be tested;
- Too many dependencies;
- Small changes breaking unrelated pages.
Fix: build in stages and verify each stage.
Asking to âmake it prettierâ
Replace vague design requests with measurable constraints:
```text
Use a neutral productivity-dashboard style.
Use one primary accent color.
Maintain at least 4.5:1 text contrast.
Keep content width below 1120px.
Use 16â24px spacing.
Create a mobile bottom navigation below 640px.
```
Letting the agent thrash after an error
If several configuration files are being changed without progress:
```text
Stop editing. Explain why the previous fix failed, which assumption was wrong, and what the minimal next experiment should be.
```
Letting the agent âfixâ the tests
The agent may weaken assertions or skip cases.
Use:
```text
Do not weaken, skip, or delete tests unless the documented requirement changed.
```
Treating a preview as production
A preview URL does not automatically include:
- Custom domains;
- Monitoring;
- Backups;
- Security headers;
- Privacy compliance;
- Reliable service guarantees;
- A formal rollback process.
19. Is Windsurf suitable for non-programmers?
It lowers the entry barrier, but it does not remove engineering judgment.
A beginner should still understand:
- Components;
- State versus props;
- Browser versus server;
- Why secrets cannot live in frontend code;
- What npm dependencies do;
- The different jobs of types, linting, and tests;
- Basic Git and deployment.
AI is highly effective for:
- Boilerplate;
- Repetitive edits;
- Code explanation;
- Error diagnosis;
- Test drafts;
- Documentation;
- Small refactors.
Humans remain responsible for:
- Product requirements;
- Architecture;
- Security;
- Acceptance criteria;
- Critical code review;
- Release decisions.
20. Delivery checklist
Functionality
- [ ] Every route works
- [ ] Validation works
- [ ] Status changes and deletion work
- [ ] Data survives reload
- [ ] A 404 page exists
- [ ] Theme preference persists
Quality
- [ ] Lint passes
- [ ] TypeScript passes
- [ ] Unit tests pass
- [ ] Critical E2E flows pass
- [ ] Production build passes
- [ ] No high-severity production dependency issues
User experience
- [ ] No mobile horizontal overflow
- [ ] Main flows work by keyboard
- [ ] Icon buttons have accessible names
- [ ] Errors are understandable
- [ ] Loading and empty states exist
- [ ] Contrast is acceptable
Security and deployment
- [ ] `.env` is ignored
- [ ] No secrets are committed
- [ ] Secret APIs use a backend boundary
- [ ] Production deployment belongs to your account
- [ ] Environment variables are configured in the host
- [ ] Logs and rollback are available
21. Final assessment
Windsurf can turn frontend development from constant switching among an editor, terminal, browser, and documentation into a continuous agent-assisted workflow.
The effective approach is not:
Build the entire website for me.
It is:
1. Store requirements in the repository;
2. Define stable rules in `AGENTS.md`;
3. Ask for a plan before edits;
4. Implement data, routing, components, styling, and tests in stages;
5. Review every diff;
6. Verify through linting, types, tests, and production builds;
7. Deploy only after the checks pass.
The reproducible project finished with:
- Zero lint issues;
- Zero TypeScript errors;
- Four passing unit tests;
- A successful production build;
- 105.79 KB of gzipped main JavaScript;
- Zero known production dependency vulnerabilities.
That does not prove that AI replaces frontend engineers.
It demonstrates something more useful:
When requirements, rules, and verification commands are explicit, Windsurf can evolve from a code generator into an agent that participates in a real delivery process.
Sources
1. [Windsurf / Devin Desktop Getting Started](https://docs.devin.ai/desktop/getting-started)
2. [Cascade Overview](https://docs.devin.ai/desktop/cascade/cascade)
3. [Memories, Rules and AGENTS.md](https://docs.devin.ai/desktop/cascade/memories)
4. [Windsurf Terminal and Auto-Execution Levels](https://docs.devin.ai/desktop/terminal)
5. [Windsurf App Deploys](https://docs.devin.ai/desktop/cascade/app-deploys)
6. [Windsurf Plans and Usage](https://docs.devin.ai/desktop/accounts/usage)
7. [Windsurf Pricing Update, March 2026](https://devin.ai/blog/windsurf-pricing-plans)
8. [Vite Getting Started](https://vite.dev/guide/)
9. [Playwright Installation and Overview](https://playwright.dev/docs/intro)