Overview
Next.js 15 App Router structure
Frontend Overview
The Prompt Stack frontend is built with Next.js 15, React 19, and TypeScript. It's designed for rapid development with pre-built components and patterns.
Technology Stack
Core Technologies
- ā” Next.js 15 (App Router)
- āļø React 19 RC
- š TypeScript 5.3+
- šØ Tailwind CSS 3.4
- š Supabase Auth
Key Libraries
- š SWR (data fetching)
- š Framer Motion
- šÆ Lucide Icons
- š next-themes
- š§© Radix UI primitives
App Router Structure
app/ āāā (authenticated)/ # Protected routes group ā āāā layout.tsx # Auth wrapper ā āāā dashboard/ # User dashboard ā āāā profile/ # User profile ā āāā settings/ # App settings āāā auth/ # Public auth pages ā āāā login/ ā āāā register/ ā āāā callback/ āāā api/ # API route handlers āāā layout.tsx # Root layout āāā page.tsx # Homepage āāā globals.css # Global styles
Creating Pages
Basic Page
Create a new file at app/my-page/page.tsx
:
export default function MyPage() {
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold mb-4">My Page</h1>
<p>This page is automatically available at /my-page</p>
</div>
)
}
Protected Page
Create a file at app/(authenticated)/my-feature/page.tsx
:
'use client'
import { useAuth } from '@/components/providers/auth-provider'
export default function MyFeaturePage() {
const { user, loading } = useAuth()
if (loading) {
return <div>Loading...</div>
}
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold mb-4">
Welcome, {user?.email}
</h1>
<p>This page requires authentication</p>
</div>
)
}
š Security Note: Pages in the (authenticated)
folder are automatically protected. The layout handles redirects for unauthenticated users.
Component Architecture
Component Organization
components/ āāā ui/ # Basic UI components ā āāā button.tsx ā āāā card.tsx ā āāā input.tsx ā āāā alert.tsx āāā forms/ # Form components ā āāā Input.tsx ā āāā Checkbox.tsx ā āāā FormGroup.tsx āāā layout/ # Layout components ā āāā navigation.tsx ā āāā footer.tsx āāā providers/ # React Context providers āāā auth-provider.tsx āāā theme-provider.tsx
Creating a Component
// components/ui/feature-card.tsx
interface FeatureCardProps {
title: string
description: string
icon?: React.ReactNode
href?: string
}
export function FeatureCard({
title,
description,
icon,
href
}: FeatureCardProps) {
const content = (
<>
{icon && (
<div className="mb-4 text-accent">
{icon}
</div>
)}
<h3 className="text-lg font-semibold mb-2">
{title}
</h3>
<p className="text-sm text-muted-foreground">
{description}
</p>
</>
)
if (href) {
return (
<Link
href={href}
className="block p-6 border rounded-lg hover:shadow-lg transition-shadow"
>
{content}
</Link>
)
}
return (
<div className="p-6 border rounded-lg">
{content}
</div>
)
}
State Management
Local State
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(false)
Global State (Context)
// Using the auth context
const { user, signIn, signOut } = useAuth()
// Using the theme context
const { theme, setTheme } = useTheme()
Server State (SWR)
import useSWR from 'swr'
function Profile() {
const { data, error, isLoading } = useSWR(
'/api/user/profile',
fetcher
)
if (error) return <div>Failed to load</div>
if (isLoading) return <div>Loading...</div>
return <div>Hello {data.name}!</div>
}
Styling
Tailwind CSS
Use utility classes for styling:
<div className="max-w-4xl mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-foreground mb-4">
Title
</h1>
<p className="text-muted-foreground">
Description text
</p>
<button className="mt-4 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90">
Click me
</button>
</div>
Dark Mode
All components support dark mode automatically:
// Colors adapt to theme
<div className="bg-background text-foreground">
<div className="bg-muted text-muted-foreground">
Subtle background
</div>
<div className="border border-border">
With border
</div>
</div>
Data Fetching
Client-Side Fetching
'use client'
export default function ClientPage() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
}, [])
return <div>{data}</div>
}
Server-Side Fetching
// Server component (default)
async function getData() {
const res = await fetch('http://localhost:8000/api/data')
return res.json()
}
export default async function ServerPage() {
const data = await getData()
return <div>{data}</div>
}
Forms
Basic Form Example
'use client'
import { useState } from 'react'
import { Input } from '@/components/forms'
export default function ContactForm() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setLoading(true)
setError('')
const formData = new FormData(e.currentTarget)
try {
const res = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message')
})
})
const data = await res.json()
if (!data.success) {
throw new Error(data.message)
}
// Success!
alert('Message sent!')
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<Input
name="name"
label="Name"
required
/>
<Input
name="email"
type="email"
label="Email"
required
/>
<textarea
name="message"
className="w-full px-3 py-2 border rounded-md"
rows={4}
required
/>
{error && (
<div className="text-red-500 text-sm">{error}</div>
)}
<button
type="submit"
disabled={loading}
className="px-4 py-2 bg-primary text-white rounded-md disabled:opacity-50"
>
{loading ? 'Sending...' : 'Send Message'}
</button>
</form>
)
}
Error Handling
Error Boundaries
Create an error.tsx
file in any route folder:
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px]">
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
<p className="text-muted-foreground mb-4">{error.message}</p>
<button
onClick={reset}
className="px-4 py-2 bg-primary text-white rounded-md"
>
Try again
</button>
</div>
)
}
Performance Tips
š
Use Server Components by default
Only add 'use client' when you need interactivity
š¦
Lazy load heavy components
Use dynamic imports for large components
š¼ļø
Optimize images
Use next/image for automatic optimization
ā”
Minimize client-side JavaScript
Server components = faster page loads
šÆ Frontend Best Practices
- Always use TypeScript for type safety
- Follow the component patterns in the codebase
- Use Tailwind utilities instead of custom CSS
- Keep components small and focused
- Handle loading and error states properly