Frontend Development Guidelines
Purpose
Comprehensive guide for modern Next.js 15 development with React 19, emphasizing Server Components, Client Components, App Router patterns, Shadcn/ui components, proper file organization, and performance optimization.
When to Use This Skill
- Creating new components or pages
- Building new features
- Fetching data (Server Components, Server Actions)
- Setting up routing with Next.js App Router
- Styling components with Tailwind CSS and Shadcn/ui
- Performance optimization
- Organizing frontend code
- TypeScript best practices
Quick Start
New Component Checklist
Creating a component? Follow this checklist:
- Determine Server vs Client Component (default: Server Component)
- Add
"use client"directive only if needed (interactivity, hooks, browser APIs) - Use TypeScript with explicit prop types
- Import Shadcn/ui components from
@/components/ui - Use Tailwind CSS classes for styling
- Import aliases:
@/components,@/lib,@/hooks - Use
cn()utility for conditional classes - Default export at bottom
- Use Server Components for data fetching when possible
New Page Checklist
Creating a page? Set up this structure:
- Create
app/{route-name}/page.tsxfor route - Use Server Component by default
- Fetch data directly in Server Component
- Create
components/directory for page-specific components - Use
loading.tsxfor loading states - Use
error.tsxfor error boundaries - Export metadata for SEO
Import Aliases Quick Reference
| Alias | Resolves To | Example |
|---|---|---|
@/ | Project root | import { cn } from '@/lib/utils' |
@/components | components/ | import { Button } from '@/components/ui/button' |
@/lib | lib/ | import { cn } from '@/lib/utils' |
@/hooks | hooks/ | import { useMobile } from '@/hooks/use-mobile' |
@/app | app/ | import { Metadata } from 'next' |
Defined in: tsconfig.json paths configuration
Common Imports Cheatsheet
// Next.js
import { Metadata } from 'next'
import { Suspense } from 'react'
import { notFound, redirect } from 'next/navigation'
// React (Client Components only)
;('use client')
import { useState, useCallback, useMemo } from 'react'
// Shadcn/ui Components
import { Button } from '@/components/ui/button'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
// Utilities
import { cn } from '@/lib/utils'
// Hooks (Client Components only)
import { useMobile } from '@/hooks/use-mobile'
// Types
import type { ComponentProps } from 'react'
Topic Guides
🎨 Component Patterns
Server Components vs Client Components:
- Server Components (default): No
"use client", can fetch data directly, smaller bundle - Client Components: Add
"use client"for interactivity, hooks, browser APIs
Key Concepts:
- Default to Server Components
- Only use Client Components when necessary
- Use Shadcn/ui components (already Client Components)
- Component structure: Props → Data Fetching → Render → Export
Example Server Component:
// app/features/posts/components/PostList.tsx
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
interface PostListProps {
posts: Post[]
}
export function PostList({ posts }: PostListProps) {
return (
<div className='grid gap-4'>
{posts.map((post) => (
<Card key={post.id}>
<CardHeader>
<CardTitle>{post.title}</CardTitle>
</CardHeader>
<CardContent>{post.content}</CardContent>
</Card>
))}
</div>
)
}
Example Client Component:
// app/features/posts/components/PostForm.tsx
'use client'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
export function PostForm() {
const [title, setTitle] = useState('')
return (
<form>
<Input value={title} onChange={(e) => setTitle(e.target.value)} />
<Button type='submit'>Submit</Button>
</form>
)
}
📊 Data Fetching
PRIMARY PATTERN: Server Components
- Fetch data directly in Server Components
- Use
async/awaitin Server Components - No need for
useEffector data fetching libraries - Automatic request deduplication
Server Actions:
- Use for mutations (forms, updates)
- Create
app/actions/directory - Mark with
"use server"directive
Example Server Component with Data Fetching:
// app/posts/page.tsx
import { PostList } from '@/components/PostList'
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store', // or 'force-cache', 'revalidate'
})
return res.json()
}
export default async function PostsPage() {
const posts = await getPosts()
return <PostList posts={posts} />
}
Example Server Action:
// app/actions/posts.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
// ... validation and creation logic
redirect('/posts')
}
📁 File Organization
App Router Structure:
app/
(routes)/
page.tsx # Route page
layout.tsx # Route layout
loading.tsx # Loading UI
error.tsx # Error UI
components/ # Shared components
ui/ # Shadcn/ui components
features/ # Feature-specific code
posts/
components/ # Feature components
actions/ # Server Actions
types/ # TypeScript types
lib/
utils.ts # Utilities (cn, etc.)
hooks/
use-mobile.ts # Custom hooks (Client only)
Feature Organization:
app/features/{feature}/: Feature-specific pages/routescomponents/: Truly reusable componentscomponents/ui/: Shadcn/ui components (don't modify directly)
🎨 Styling
Tailwind CSS + Shadcn/ui:
- Use Tailwind utility classes
- Use
cn()utility for conditional classes - Shadcn/ui components use CSS variables for theming
- Customize theme in
app/globals.css
Styling Patterns:
import { cn } from '@/lib/utils'
interface ButtonProps {
variant?: 'primary' | 'secondary'
className?: string
}
export function Button({ variant = 'primary', className }: ButtonProps) {
return (
<button
className={cn(
'rounded-md px-4 py-2',
variant === 'primary' && 'bg-primary text-primary-foreground',
variant === 'secondary' && 'bg-secondary text-secondary-foreground',
className,
)}
>
Click me
</button>
)
}
Shadcn/ui Components:
- Import from
@/components/ui/{component-name} - Components are already styled and accessible
- Customize via
classNameprop or CSS variables
🛣️ Routing
Next.js App Router - File-Based:
- Directory:
app/{route-name}/page.tsx - Nested routes:
app/{parent}/{child}/page.tsx - Dynamic routes:
app/posts/[id]/page.tsx - Route groups:
app/(marketing)/about/page.tsx
Example Route:
// app/posts/page.tsx
import { Metadata } from 'next'
import { PostList } from '@/components/PostList'
export const metadata: Metadata = {
title: 'Posts',
description: 'List of all posts',
}
export default async function PostsPage() {
const posts = await getPosts()
return (
<div className='container mx-auto py-8'>
<h1 className='text-3xl font-bold mb-6'>Posts</h1>
<PostList posts={posts} />
</div>
)
}
Dynamic Route:
// app/posts/[id]/page.tsx
interfac