Frontend Builder
Skill2 starsChanged 6 months agoMITView on GitHub
Build modern React/Next.js frontends. Use when creating web applications, choosing frontend stack, structuring components, or implementing UI/UX designs. Covers React, Next.js, Tailwind CSS, and component patterns.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- tmcfarlane/team-avatarword for word, first seen Oct 20, 2026
Other versions1 project uses a version of this.
Word for word · 1
- tmcfarlane/team-avatar★ 112100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository16 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .cursor/rules/orchestrator.mdcCursor rule
- architectSkill
- codebase-searchSkill
- debuggingSkill
- design-patterns-implementationSkill
- documentation-writingSkill
- mgrep-code-searchSkill
- planningSkill
- refactoring-patternsSkill
- RefactoringSkill
- technical-roadmap-planningSkill
- vercel-composition-patternsSkill
- vercel-react-best-practicesSkill
- web-design-guidelinesSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Keep state as close to where it's used as possible
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Frontend Builder
Build maintainable, performant React and Next.js frontends.
Core Principles
1. Component Composition
Break UI into small, reusable, single-purpose components
2. State Proximity
Keep state as close to where it's used as possible
3. Performance by Default
Optimize rendering, code splitting, and asset loading
4. Developer Experience
Clear naming, consistent patterns, helpful errors
Framework Selection
React (Vite) vs. Next.js
Use React + Vite when:
- Client-side only application
- No SEO requirements
- Simple deployment (static hosting)
- Faster initial setup
Use Next.js when:
- SEO important (marketing sites, blogs, e-commerce)
- Server-side rendering needed
- API routes required
- File-based routing preferred
- Image optimization critical
Recommended for most projects: Next.js (App Router)
Component Architecture
Component Types
1. Page Components (Route entry points):
// app/users/page.tsx (Next.js App Router)
export default function UsersPage() {
return (
<div>
<Header />
<UserList />
<Footer />
</div>
)
}2. Feature Components (Business logic):
// components/features/UserList.tsx
export function UserList() {
const { data, isLoading } = useUsers()
if (isLoading) return <LoadingSpinner />
return (
<div>
{data.map(user => <UserCard key={user.id} user={user} />)}
</div>
)
}3. UI Components (Reusable, no business logic):
// components/ui/button.tsx
export function Button({ children, variant = 'primary', ...props }) {
return (
<button
className={cn(buttonVariants[variant])}
{...props}
>
{children}
</button>
)
}Component Best Practices
// ✅ Good: Small, focused, typed
interface UserProfileProps {
user: User
onEdit?: () => void
}
export function UserProfile({ user, onEdit }: UserProfileProps) {
return (
<div className="flex gap-4">
<Avatar src={user.avatar} alt={user.name} />
<UserDetails user={user} />
{onEdit && <Button onClick={onEdit}>Edit</Button>}
</div>
)
}
// ❌ Bad: Giant, untyped, unclear
export function UserProfile(props) {
// 500 lines of JSX, multiple responsibilities
return <div>...</div>
}State Management
Decision Tree
How many components need this state?
│
├─ One component → useState
├─ Parent + children → Props or useState + props
├─ Siblings → Lift to common parent
├─ Widely used (theme, auth) → Context API
└─ Complex app state → Zustand or ReduxLocal State (useState)
// For component-level state
function Counter() {
const [count, setCount] = useState(0)
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
)
}Context API
// For app-wide state (theme, auth, user)
const UserContext = createContext<UserContextType | undefined>(undefined)
export function UserProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
)
}
export function useUser() {
const context = useContext(UserContext)
if (!context) throw new Error('useUser must be within UserProvider')
return context
}Zustand (Recommended for Complex State)
import { create } from 'zustand'
interface CounterStore {
count: number
increment: () => void
decrement: () => void
reset: () => void
}
export const useCounterStore = create<CounterStore>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}))
// Usage
function Counter() {
const { count, increment } = useCounterStore()
return <button onClick={increment}>{count}</button>
}Data Fetching
React Query (Recommended)
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
// Query (GET)
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000 // 5 minutes
})
if (isLoading) return <LoadingSpinner />
if (error) return <ErrorMessage error={error} />
return <UserList users={data} />
}
// Mutation (POST, PUT, DELETE)
function CreateUser() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] })
}
})
return (
<button onClick={() => mutation.mutate({ name: 'John' })}>
Create User
</button>
)
}Next.js Server Components (App Router)
// app/users/page.tsx
// Server Component - fetches on server
export default async function UsersPage() {
const users = await fetchUsers() // Runs on server
return <UserList users={users} />
}
// Client Component - for interactivity
'use client'
export function UserList({ users }: { users: User[] }) {
const [selected, setSelected] = useState<string | null>(null)
return (
<div>
{users.map(user => (
<UserCard
key={user.id}
user={user}
onClick={() => setSelected(user.id)}
/>
))}
</div>
)
}Form Handling
React Hook Form (Recommended)
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters')
})
type LoginForm = z.infer<typeof loginSchema>
function LoginForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting }
} = useForm<LoginForm>({
resolver: zodResolver(loginSchema)
})
const onSubmit = async (data: LoginForm) => {
await login(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('email')}
type="email"
placeholder="Email"
className="border p-2"
/>
{errors.email && (
<span className="text-red-500">{errors.email.message}</span>
)}
</div>
<div>
<input
{...register('password')}
type="password"
placeholder="Password"
className="border p-2"
/>
{errors.password && (
<span className="text-red-500">{errors.password.message}</span>
)}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Logging in...' : 'Login'}
</button>
</form>
)
}Styling
Tailwind CSS (Recommended)
// Install: @shadcn/ui for component library
function Button({ variant = 'primary', children, ...props }) {
return (
<button
className={cn(
'px-4 py-2 rounded font-medium transition-colors',
{
'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
'bg-gray-200 text-gray-900 hover:bg-gray-300': variant === 'secondary',
'bg-red-500 text-white hover:bg-red-600': variant === 'danger'
}
)}
{...props}
>
{children}
</button>
)
}CSS Modules (Alternative)
// Button.module.css
.button {
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
.primary {
background-color: blue;
color: white;
}
// Button.tsx
import styles from './Button.module.css'
export function Button({ variant = 'primary', children }) {
return (
<button className={`${styles.button} ${styles[variant]}`}>
{children}
</button>
)
}Performance Optimization
React Optimization
import { memo, useMemo, useCallback } from 'react'
// 1. Memoize expensive calculations
function DataTable({ data }) {
const sortedData = useMemo(
() => data.sort((a, b) => a.name.localeCompare(b.name)),
[data]
)
return <Table data={sortedData} />
}
// 2. Memoize callbacks
function Parent() {
const handleClick = useCallback(() => {
console.log('Clicked')
}, [])
return <ExpensiveChild onClick={handleClick} />
}
// 3. Memoize components
const ExpensiveChild = memo(function ExpensiveChild({ onClick }) {
return <button onClick={onClick}>Click</button>
})Next.js Optimization
// 1. Image optimization
import Image from 'next/image'
<Image
src="/photo.jpg"
alt="Photo"
width={500}
height={300}
priority // Above the fold
/>
// 2. Font optimization
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }) {
return (
<html className={inter.className}>
<body>{children}</body>
</html>
)
}
// 3. Dynamic imports (code splitting)
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <LoadingSpinner />
})Error Handling
Error Boundary
'use client'
import { Component, ReactNode } from 'react'
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="p-4 bg-red-50 border border-red-200">
<h2 className="text-red-800">Something went wrong</h2>
<p className="text-red-600">{this.state.error?.message}</p>
</div>
)
}
return this.props.children
}
}Next.js Error Handling
// app/error.tsx
'use client'
export default function Error({
error,
reset
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
)
}Folder Structure
Next.js App Router
app/
├── (auth)/ # Route group (auth pages)
│ ├── login/
│ └── signup/
├── (dashboard)/ # Route group (dashboard)
│ ├── layout.tsx
│ ├── page.tsx
│ └── settings/
├── api/ # API routes
│ └── users/
│ └── route.ts
└── layout.tsx # Root layout
components/
├── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── input.tsx
│ └── dialog.tsx
├── features/ # Feature components
│ ├── UserList.tsx
│ └── UserProfile.tsx
└── layouts/ # Layout components
├── Header.tsx
└── Footer.tsx
lib/
├── utils.ts # Utility functions
├── api.ts # API client
└── validation.ts # Zod schemas
hooks/
├── useUser.ts
└── useDebounce.ts
stores/
└── userStore.ts # Zustand storesTypeScript Best Practices
// 1. Type component props
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger'
children: ReactNode
onClick?: () => void
}
export function Button({ variant = 'primary', children, onClick }: ButtonProps) {
return <button onClick={onClick}>{children}</button>
}
// 2. Type API responses
interface User {
id: string
name: string
email: string
}
async function fetchUsers(): Promise<User[]> {
const res = await fetch('/api/users')
return res.json()
}
// 3. Type state
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(false)Summary
Great frontends:
- ✅ Use Next.js for most projects (SEO, performance, DX)
- ✅ Break UI into small, typed components
- ✅ Choose appropriate state management (useState → Context → Zustand)
- ✅ Use React Query for server state
- ✅ Style with Tailwind CSS + shadcn/ui
- ✅ Optimize with memoization and code splitting
- ✅ Handle errors gracefully with Error Boundaries
- ✅ Follow consistent folder structure
Related Resources
Related Skills:
api-designer- For designing backend APIs to consumeux-designer- For creating UX designs to implementdeployment-advisor- For hosting Next.js/React apps
Related Patterns:
META/DECISION-FRAMEWORK.md- Frontend framework selectionSTANDARDS/architecture-patterns/component-patterns.md- Component design patterns (when created)
Related Playbooks:
PLAYBOOKS/setup-nextjs-project.md- Next.js project setup (when created)PLAYBOOKS/optimize-frontend-performance.md- Performance optimization (when created)
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

