Next.js 15 documentation site with Fumadocs for curated web development resources and guides. Features a dual content system (Resources + Guides) and client-side favorites system with localStorage persistence.
# Development
bun dev # Start dev server with Turbopack
bun run build # Production build with Turbopack
bun start # Start production server
# Code Quality
bun run lint # Run ESLint
bun run format # Format with Prettier + Tailwind sorting
bun run clean # Clean .next and .source directories
bun run postinstall # Run fumadocs-mdx setupNote: No test runner is configured in this project.
Order: React → Next.js → Internal @/ imports → Relative imports
// React first
// Next.js
import { Metadata } from "next"
import { useEffect, useState } from "react"
import { useFavorites } from "@/hooks/use-favorites"
// Internal @/ imports
import { cn } from "@/lib/utils"
// Relative imports last
import "./styles.css"- Components: PascalCase (
CustomCard,FavoritesButton) - Hooks: camelCase with
useprefix (useFavorites,useMeasure) - Utilities: camelCase (
cn,generateFavoriteId) - Types: PascalCase with descriptive names (
FavoriteResource,CreateFavoriteInput) - Files: kebab-case for non-component files, PascalCase for components
- Constants: UPPER_SNAKE_CASE for true constants (
STORAGE_KEY,FAVORITES_EVENT)
- Use explicit return types on exported functions
- Prefer interfaces for object shapes, types for unions/complex types
- Use
typeimports when only importing types:import type { Favorite } from "@/types" - Enable strict mode (enabled in tsconfig)
Use cn() utility from @/lib/utils to combine classes. Prettier auto-sorts classes via prettier-plugin-tailwindcss.
className={cn(
// Base styles - automatically sorted by Prettier
"inline-flex h-10 items-center justify-center rounded-md px-4 py-2 text-sm font-medium",
// Variant styles
variant === "default" && "bg-primary text-white hover:bg-primary-focus",
// Responsive styles - automatically sorted
"w-full text-sm sm:w-auto md:text-base",
// State styles
disabled && "cursor-not-allowed opacity-50",
// External classes always last
className,
)}- Use try/catch for localStorage operations (check
typeof windowfirst) - Log errors to console with descriptive messages
- Return safe defaults on failure (empty arrays, null values)
try {
const stored = localStorage.getItem(STORAGE_KEY)
if (!stored) return []
return JSON.parse(stored)
} catch (error) {
console.error("Failed to load favorites:", error)
return []
}Mark client components explicitly at the top:
"use client"
import { useEffect, useState } from "react"content/{guides|resources}/*.mdx → .source/index.ts → lib/source.ts → API routes
- Update
meta.jsonfiles to define navigation structure - Use
CustomCardcomponent in MDX for external resource links
- Hook:
useFavorites()for state management and persistence - Storage key:
ca-resources-favorites - Cross-component sync via custom event:
favorites-updated - ID generation: Deterministic from
title + href
- Always check
typeof window !== "undefined"before accessing browser APIs - Handle localStorage operations in try/catch blocks
- External links should include
?ref=ca-resources.vercel.app
lib/source.ts- Content source configurationsource.config.ts- Fumadocs collection definitionshooks/use-favorites.ts- Favorites state managementapp/layout.config.tsx- Navigation and brandinglib/utils.ts-cn()utility for Tailwind
See .github/copilot-instructions.md and .github/instructions/styling.instructions.md for full guidelines on Fumadocs integration, favorites system, and Tailwind class organization.