Web Frameworks

中级 Intermediate 参考型 Reference claude-code
14 min read · 682 lines

Build modern full-stack web apps with Next.js, Turborepo, and RemixIcon

Web Frameworks

Overview

Comprehensive guide for building modern full-stack web applications using Next.js, Turborepo, and RemixIcon. Covers the complete lifecycle from project setup to production deployment.

  • Next.js - React framework with SSR, SSG, RSC, and built-in optimizations
  • Turborepo - High-performance monorepo build system for JavaScript/TypeScript
  • RemixIcon - Icon library with 3,100+ stroke and fill style icons

When to Use

  • Building new full-stack web applications with modern React
  • Setting up monorepos with multiple apps and shared packages
  • Implementing server-side rendering (SSR) and static generation (SSG)
  • Optimizing build performance with smart caching
  • Creating consistent UI with a professional icon system
  • Managing workspace dependencies across projects
  • Deploying production-ready applications with proper optimization

Stack Selection Guide

Standalone App: Next.js + RemixIcon

For building individual applications:

  • E-commerce sites
  • Marketing websites
  • SaaS applications
  • Documentation sites
  • Blogs and content platforms

Setup:

npx create-next-app@latest my-app
cd my-app
npm install remixicon

Monorepo: Next.js + Turborepo + RemixIcon

For building multiple apps sharing code:

  • Microfrontends
  • Multi-tenant platforms
  • Internal tools with shared component libraries
  • Multiple apps (web, admin, mobile-web) sharing logic
  • Design systems + documentation sites

Setup:

npx create-turbo@latest my-monorepo
# Then configure Next.js apps in apps/
# Install remixicon in shared UI package

Framework Feature Comparison

Feature Next.js Turborepo RemixIcon
Primary Use Web framework Build system UI icons
Best For SSR/SSG apps Monorepo Consistent icon system
Performance Built-in optimizations Caching & parallel tasks Lightweight font/SVG
TypeScript Full support Full support Type definitions available

Quick Start

Next.js App

# Create new project
npx create-next-app@latest my-app
cd my-app

# Install RemixIcon
npm install remixicon

# Import in layout
# app/layout.tsx
import 'remixicon/fonts/remixicon.css'

# Start development
npm run dev

Turborepo Monorepo

# Create monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo

# Directory structure:
# apps/web/          - Next.js app
# apps/docs/         - Documentation site
# packages/ui/       - Shared components with RemixIcon
# packages/config/   - Shared configuration
# turbo.json         - Pipeline configuration

# Run all apps
npm run dev

# Build all packages
npm run build

RemixIcon Integration

// Web font approach (HTML/CSS)
<i className="ri-home-line"></i>
<i className="ri-search-fill ri-2x"></i>

// React component approach
import { RiHomeLine, RiSearchFill } from "@remixicon/react"
<RiHomeLine size={24} />
<RiSearchFill size={32} color="blue" />

Next.js App Router Architecture

File Conventions

Special files define route behavior:

File Purpose
page.tsx Page UI, makes route publicly accessible
layout.tsx Shared UI wrapper for segment and child routes
loading.tsx Loading UI, auto-wraps page in Suspense
error.tsx Error UI, wraps page in Error Boundary
not-found.tsx 404 UI for route segment
route.ts API endpoint (Route Handler)
template.tsx Re-rendered layout (no state preservation)
default.tsx Fallback page for parallel routes

Basic Routing

app/
+-- page.tsx              -> /
+-- about/
|   +-- page.tsx         -> /about
+-- blog/
|   +-- page.tsx         -> /blog
+-- contact/
    +-- page.tsx         -> /contact

Dynamic Routes

// app/blog/[slug]/page.tsx - Single parameter
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>Post: {params.slug}</h1>
}
// Matches: /blog/hello-world, /blog/my-post

// app/shop/[...slug]/page.tsx - Catch-all segment
// Matches: /shop/clothes, /shop/clothes/shirts, /shop/clothes/shirts/red

// app/docs/[[...slug]]/page.tsx - Optional catch-all
// Matches: /docs, /docs/getting-started, /docs/api/reference

Layouts

Root Layout (Required)

Must include <html> and <body> tags:

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <header>Global Header</header>
        {children}
        <footer>Global Footer</footer>
      </body>
    </html>
  )
}

Nested Layouts

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div>
      <nav>Dashboard Navigation</nav>
      <main>{children}</main>
    </div>
  )
}

Layout characteristics:

  • Preserve state during navigation
  • Don't re-render when navigating between child routes
  • Can fetch data
  • Cannot access pathname or searchParams (use client components)

Route Groups

Organize routes without affecting URL structure:

app/
+-- (marketing)/          # Group (doesn't affect URL)
|   +-- about/page.tsx   -> /about
|   +-- blog/page.tsx    -> /blog
+-- (shop)/
|   +-- products/page.tsx -> /products
|   +-- cart/page.tsx     -> /cart
+-- layout.tsx           # Root layout

Parallel Routes

Render multiple pages simultaneously in the same layout:

// app/layout.tsx
export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode
  team: React.ReactNode
  analytics: React.ReactNode
}) {
  return (
    <>
      {children}
      <div className="grid grid-cols-2">
        {team}
        {analytics}
      </div>
    </>
  )
}

Loading States

// app/dashboard/loading.tsx - Auto-wrapped in Suspense
export default function Loading() {
  return <div className="spinner">Loading dashboard...</div>
}

// Manual Suspense - finer control
import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <h1>My Blog</h1>
      <Suspense fallback={<div>Loading posts...</div>}>
        <Posts />
      </Suspense>
    </div>
  )
}

Error Handling

// app/error.tsx - Must be client component
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Try again</button>
    </div>
  )
}

Navigation

// Link component
import Link from 'next/link'
<Link href="/about">About</Link>
<Link href={`/blog/${post.slug}`}>Read Post</Link>

// useRouter hook (client-side)
'use client'
import { useRouter } from 'next/navigation'
const router = useRouter()
router.push('/dashboard')   // Navigate forward
router.replace('/login')     // Replace current
router.refresh()             // Refresh
router.back()                // Go back

// Server-side redirect
import { redirect } from 'next/navigation'
if (!session) {
  redirect('/login')
}

Server Components

Core Concepts

All components in app/ are server components by default:

// app/posts/page.tsx - Server component
async function getPosts() {
  const res = await fetch('https://api.example.com/posts')
  return res.json()
}

export default async function PostsPage() {
  const posts = await getPosts()

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>{post.title}</article>
      ))}
    </div>
  )
}

Advantages:

  • Fetch data on the server (direct database access)
  • Keep sensitive data/secrets server-side
  • Reduce client JavaScript bundle size
  • Improve initial load and SEO
  • Cache results on the server
  • Stream content to the client

Limitations:

  • Cannot use React hooks (useState, useEffect, useContext)
  • Cannot use browser APIs (window, localStorage)
  • Cannot add event listeners (onClick, onChange)

Client Components

Mark with 'use client' directive at the top of the file:

// components/counter.tsx - Client component
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}

Use client components for:

  • Interactive UI (event handlers)
  • State management (useState, useReducer)
  • Side effects (useEffect, useLayoutEffect)
  • Browser-only APIs (localStorage, geolocation)
  • Custom React hooks
  • Context consumers

Composition Pattern

Best practice: Keep server components as parents, pass client components as children.

// app/page.tsx - Server component
import { ClientProvider } from './client-provider'
import { ServerContent } from './server-content'

export default function Page() {
  return (
    <ClientProvider>
      <ServerContent /> {/* Stays as server component */}
    </ClientProvider>
  )
}

Server Actions

Call server functions from client components:

// app/actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  const content = formData.get('content') as string

  await db.post.create({ data: { title, content } })
  revalidatePath('/posts')
}

// Usage
<form action={createPost}>
  <input name="title" required />
  <textarea name="content" required />
  <button type="submit">Create Post</button>
</form>

Suspense Streaming

import { Suspense } from 'react'

async function SlowComponent() {
  await new Promise(resolve => setTimeout(resolve, 3000))
  return <div>Loaded after 3 seconds</div>
}

export default function Page() {
  return (
    <div>
      <h1>Instantly rendered heading</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <SlowComponent />
      </Suspense>
    </div>
  )
}

Benefits:

  • Fast components render immediately
  • Slow components don't block the page
  • Progressive enhancement
  • Better perceived performance

When to Use Which Component Type

Use Server Components Use Client Components
Fetch data from DB or API Add interactivity (onClick, onChange)
Access backend resources directly Manage state (useState)
Keep sensitive info server-side Use lifecycle effects (useEffect)
Reduce client JavaScript Use browser-only APIs
Render static content Use custom React hooks
No interactivity needed Use React Context

Data Fetching

Optimized Data Fetching

// app/posts/[slug]/page.tsx
import { notFound } from 'next/navigation'

// Static generation at build time
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map(post => ({ slug: post.slug }))
}

// Revalidate every hour
async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 3600 }
  })
  if (!res.ok) return null
  return res.json()
}

export default async function Post({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  if (!post) notFound()

  return <article>{post.content}</article>
}

Caching Strategies

// Force cache (default)
fetch(url, { cache: 'force-cache' })

// Time-based revalidation
fetch(url, { next: { revalidate: 3600 } }) // Every hour

// No caching
fetch(url, { cache: 'no-store' })

Turborepo

Project Structure

my-monorepo/
+-- apps/
|   +-- web/              # Customer-facing Next.js app
|   +-- admin/            # Admin panel Next.js app
|   +-- docs/             # Documentation site
+-- packages/
|   +-- ui/               # Shared UI with RemixIcon
|   +-- api-client/       # API client library
|   +-- config/           # ESLint, TypeScript configs
|   +-- types/            # Shared TypeScript types
+-- turbo.json            # Build pipeline

Pipeline Configuration

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {},
    "test": {
      "dependsOn": ["build"]
    }
  }
}

Shared Component Library Pattern

// packages/ui/src/button.tsx
import { RiLoader4Line } from "@remixicon/react"

export function Button({ children, loading, icon }) {
  return (
    <button>
      {loading ? <RiLoader4Line className="animate-spin" /> : icon}
      {children}
    </button>
  )
}

// apps/web/app/page.tsx
import { Button } from "@repo/ui/button"
import { RiHomeLine } from "@remixicon/react"

export default function Page() {
  return <Button icon={<RiHomeLine />}>Home</Button>
}

CI/CD Pipeline

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 18
      - run: npm install
      - run: npx turbo run build test lint
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ secrets.TURBO_TEAM }}

Best Practices

Next.js

  • Default to Server Components, use Client Components only when needed
  • Implement proper loading and error states
  • Use Image component for automatic optimization
  • Set up proper metadata for SEO
  • Leverage caching strategies (force-cache, revalidate, no-store)

Turborepo

  • Clean separation of monorepo structure (apps/, packages/)
  • Properly define task dependencies (^build for topological ordering)
  • Configure outputs for correct caching
  • Enable remote caching for team collaboration
  • Use filters to run tasks only on changed packages

RemixIcon

  • Use line style for minimal interfaces, fill style for emphasis
  • Maintain 24x24 grid alignment for crisp rendering
  • Provide aria-labels for accessibility
  • Use currentColor for flexible theming
  • Prefer web fonts for multiple icons, SVG for single icons

Implementation Checklist

When building with this stack:

  • Create project structure (standalone or monorepo)
  • Configure TypeScript and ESLint
  • Set up Next.js App Router
  • Configure Turborepo pipeline (if monorepo)
  • Install and configure RemixIcon
  • Implement routes and layouts
  • Add loading and error states
  • Configure image and font optimization
  • Set up data fetching patterns
  • Configure caching strategies
  • Add API routes as needed
  • Implement shared component library (if monorepo)
  • Configure remote caching (if monorepo)
  • Set up CI/CD pipeline
  • Configure deployment platform

External Resources

相关技能 Related Skills