_extra-skills/nextjs-architecture/SKILL.md
Core Skill. Next.js 15+ App Router architecture guidelines including component patterns, state management with Zustand, server actions, and project structure. Use when developing Next.js applications.
npx skillsauth add poko8nada/pj_docs nextjs-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Always consult Context7 if you plan to deviate from these guidelines.
page.tsx, layout.tsx"use client" directive at top_features/ or _components/ directories onlyServer Component (fetch data) → Client Component (props) → interactivity
Features directly import stores, hooks, and libs.
Use when:
Example:
// app/dashboard/_features/UserProfile.tsx
"use client"
import { useUserStore } from '@/store/userStore'
import { formatUserData } from '@/lib/userUtils'
export function UserProfile() {
const user = useUserStore(state => state.user)
const formatted = formatUserData(user)
return <div>{formatted}</div>
}
Trade-offs: Simple, less boilerplate | Harder to test, lower reusability
Feature handles logic, presentational component receives props.
Use when:
Example:
// app/dashboard/_features/UserProfileFeature.tsx
"use client"
import { useUserStore } from '@/store/userStore'
import { formatUserData } from '@/lib/userUtils'
import { UserProfileView } from '@/components/UserProfileView'
export function UserProfileFeature() {
const user = useUserStore(state => state.user)
const formatted = formatUserData(user)
return <UserProfileView data={formatted} />
}
// components/UserProfileView.tsx (reusable, can be Server Component)
export function UserProfileView({ data }: { data: string }) {
return <div>{data}</div>
}
Trade-offs: Testable, reusable, clear separation | More boilerplate
Start with Pattern 1. Refactor to Pattern 2 when you encounter:
lib/ or _lib/// store/userStore.ts - State only
import { create } from "zustand";
type UserState = {
user: User | null;
setUser: (user: User | null) => void;
};
export const useUserStore = create<UserState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// lib/userActions.ts - Logic (testable)
export function validateUser(user: User): Result<User, string> {
if (!user.email) return { ok: false, error: "Email required" };
return { ok: true, value: user };
}
export async function fetchAndSetUser(userId: string) {
try {
const user = await fetchUser(userId);
useUserStore.getState().setUser(user);
return { ok: true, value: user };
} catch (error) {
console.error("Fetch user error:", error);
return { ok: false, error: "Failed to fetch user" };
}
}
// ❌ Bad: Redundant state
const [users, setUsers] = useState<User[]>([]);
const [activeUsers, setActiveUsers] = useState<User[]>([]);
// ✅ Good: Derived state
const [users, setUsers] = useState<User[]>([]);
const activeUsers = users.filter((u) => u.isActive);
_prefix, live within route directories@folder for multi-part layouts[param] for dynamic segments(group) for organizing related routesapp/
├── dashboard/
│ ├── @modal/ # Parallel route
│ ├── @search/ # Parallel route
│ ├── page.tsx # Server component
│ ├── loading.tsx # Loading UI (auto-wrapped in Suspense)
│ ├── error.tsx # Error boundary
│ ├── _components/ # Route-specific UI
│ ├── _features/ # Route-specific logic
│ │ ├── DisplayUserProfile.tsx
│ │ └── UserDashboard/ # Feature with sub-features (max 1-level nesting)
│ │ ├── index.tsx
│ │ ├── UserActivityFeed.tsx # Sub-feature (1-level deep)
│ │ ├── UserStats.tsx # Sub-feature (1-level deep)
│ │ └── useUserData.ts # Feature-specific hook
│ ├── _hooks/ # Shared across route features
│ ├── _actions/ # Route-specific server actions
│ ├── _lib/ # Route-specific business logic
│ │ ├── userLogic.ts
│ │ └── userLogic.test.ts
│ ├── _store/ # Route-specific Zustand stores
│ │ └── dashboardStore.ts
│ └── _config/ # Route-specific config
├── posts/
│ ├── [slug]/ # Dynamic route
│ ├── page.tsx
│ ├── loading.tsx
│ ├── error.tsx
│ ├── _components/
│ ├── _features/
│ ├── _hooks/
│ ├── _actions/
│ ├── _lib/
│ └── _config/
├── page.tsx # Root page
├── loading.tsx # Root loading
├── error.tsx # Root error boundary
└── layout.tsx # Root layout
components/ # Global shared UI
├── ui/ # Atomic UI pieces (shadcn/ui, primitives)
├── layouts/ # Layout components (PageLayout, SectionLayout)
└── ... # Custom global components
hooks/ # Global shared hooks
lib/ # Global business logic
├── userActions.ts
├── userActions.test.ts
├── auth.ts
└── api.ts
store/ # Global Zustand stores
├── userStore.ts
└── appStore.ts
utils/ # Pure utilities only (NOT business logic)
├── format.ts # Date formatting, string manipulation, etc.
└── types.ts # Global types only (e.g., Result<T,E>)
public/ # Static assets
// ✅ Good: Layout component in components/layouts/
<PageLayout>
<ActualFeature />
</PageLayout>
// ✅ Good: Tailwind classes directly
<div className="container mx-auto p-4">
<ActualFeature />
</div>
DisplayUserProfile.tsx or DisplayUserProfile/index.tsxpage.tsx, not across same-level Featurescomponents/, hooks/, lib/, store/): Used across multiple routes_components/, _features/, _hooks/, _lib/, _store/): Used only within that routeloading.tsx)export default function Loading() {
return <div>Loading...</div>;
}
page.tsx in <Suspense>error.tsx)"use client"; // Error boundaries must be Client Components
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
reset() attempts to re-render the segmentnot-found.tsx)export default function NotFound() {
return <div>404 - Page Not Found</div>;
}
notFound() function or invalid routesPlace in route-specific _actions/ directory
"use server";
export async function createPost(
formData: FormData,
): Promise<Result<Post, string>> {
// Validation
const title = formData.get("title");
if (!title) return { ok: false, error: "Title required" };
// External operation with try-catch
try {
const post = await db.insert({ title });
revalidatePath("/posts");
return { ok: true, value: post };
} catch (error) {
console.error("DB error:", error);
return { ok: false, error: "Failed to create post" };
}
}
Result<T, E>revalidatePath() or revalidateTag() for cache invalidationtools
Composite Skill. This skill is used for project planning. Users request that a project plan be created, particularly during the initial stages.
documentation
Core Skill. This skill is for document creation. User ask you to create planning documents, such as requirement and task breakdown.
development
Core Skill. HonoX architecture guidelines including file-based routing, Islands pattern, component types, performance optimization, and best practices for full-stack development.
development
Hono + HTMX architecture guidelines for Cloudflare Workers. Use when developing server-rendered applications with Hono, HTMX, and Hono JSX.