Prompt file imported from drewsephski/convext-workflow-orchestrator (
.windsurf/workflows/create-component.md). Copyright stays with the author.
Component Creation Workflow
-
Choose Component Type
- UI Component (
components/ui/) - Reusable primitives - Feature Component (
components/feature/) - Business logic - Layout Component (
components/layout/) - Page structure
- UI Component (
-
Create Component File
# Example: Feature component touch components/users/user-card.tsx touch components/users/index.ts # Barrel export -
Component Structure
// components/users/user-card.tsx "use client"; import { useState } from "react"; import { useQuery } from "convex/react"; import { api } from "@/convex/_generated/api"; import { cn } from "@/lib/utils"; import { Avatar } from "@/components/ui/avatar"; import { Card } from "@/components/ui/card"; interface UserCardProps { userId: string; className?: string; } export function UserCard({ userId, className }: UserCardProps) { const user = useQuery(api.users.get, { id: userId }); if (user === undefined) { return <div>Loading...</div>; } if (user === null) { return <div>User not found</div>; } return ( <Card className={cn("p-4", className)}> <div className="flex items-center space-x-3"> <Avatar src={user.imageUrl} name={user.name} /> <div> <h3 className="font-semibold">{user.name}</h3> <p className="text-sm text-muted-foreground">{user.email}</p> </div> </div> </Card> ); } -
Add Barrel Export
// components/users/index.ts export { UserCard } from "./user-card"; export { UserForm } from "./user-form"; export { UserList } from "./user-list"; -
Component Testing
// components/users/__tests__/user-card.test.tsx import { render, screen } from "@testing-library/react"; import { describe, it, expect } from "vitest"; import { UserCard } from "../user-card"; describe("UserCard", () => { it("displays user information", () => { render(<UserCard userId="test-user" />); expect(screen.getByText("Loading...")).toBeInTheDocument(); }); });
Component Patterns
Server vs Client Decision
Does it need browser APIs, state, or effects?
├── YES → Client Component ("use client")
└── NO → Does it fetch data?
├── YES → Server Component (default)
└── NO → Server Component
Props Interface Naming
- Use
{ComponentName}Propspattern - Extend HTML attributes when needed
- Provide default values for optional props
Styling Guidelines
- Use
cn()utility for class merging - Follow Tailwind v4 CSS variables
- Use semantic color tokens
Import Order
- React/Next.js imports
- External packages
- Internal aliases (@/)
- Relative imports