← Back to list

nextjs-conventions
by TerryXBT
⭐ 0🍴 0📅 Jan 17, 2026
SKILL.md
name: nextjs-conventions description: Next.js 16 App Router conventions and patterns for Slotify. Use this skill when creating new pages, implementing server components, or working with the routing system. Covers file structure, data fetching, and caching.
Next.js Conventions Skill
This skill defines the Next.js 16 App Router conventions for Slotify.
App Router File Conventions
src/app/
├── layout.tsx # Root layout (wraps all pages)
├── page.tsx # Home page (/)
├── globals.css # Global styles
├── login/
│ ├── page.tsx # Login page (/login)
│ └── actions.ts # Login server actions
├── app/ # Protected routes (/app/*)
│ ├── layout.tsx # App layout with nav
│ ├── today/
│ │ └── page.tsx # Dashboard (/app/today)
│ └── settings/
│ ├── page.tsx # Settings page
│ └── actions.ts # Settings server actions
├── [username]/ # Dynamic route (/[username])
│ └── page.tsx # Public booking page
└── api/ # API routes
└── webhooks/
└── route.ts # Webhook handler
Server vs Client Components
Default: Server Components
// app/bookings/page.tsx - Server Component (default)
import { createClient } from '@/utils/supabase/server';
export default async function BookingsPage() {
const supabase = await createClient();
const { data: bookings } = await supabase
.from('bookings')
.select('*');
return <BookingList bookings={bookings} />;
}
Client Components (When Needed)
Use 'use client' only when you need:
- useState, useEffect, or other hooks
- Event handlers (onClick, onChange)
- Browser-only APIs
- Third-party client libraries
'use client';
import { useState } from 'react';
export function BookingModal() {
const [isOpen, setIsOpen] = useState(false);
// ...
}
Data Fetching Patterns
Pattern 1: Server Component Data Fetching
// Preferred for initial page load
async function getData() {
const supabase = await createClient();
return supabase.from('services').select('*');
}
export default async function Page() {
const { data } = await getData();
return <ServiceList services={data} />;
}
Pattern 2: Server Actions for Mutations
// app/settings/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function updateProfile(formData: FormData) {
const supabase = await createClient();
await supabase
.from('profiles')
.update({ name: formData.get('name') })
.eq('id', userId);
revalidatePath('/app/settings');
}
Pattern 3: Route Handlers for APIs
// app/api/availability/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const date = searchParams.get('date');
// ... fetch availability
return NextResponse.json({ slots });
}
Routing Conventions
Dynamic Routes
// app/[username]/page.tsx
export default async function UserBookingPage({
params,
}: {
params: Promise<{ username: string }>;
}) {
const { username } = await params;
// ...
}
Route Groups
Use (groupName) for organizing without affecting URL:
app/
├── (auth)/ # Auth pages (no /auth prefix in URL)
│ ├── login/
│ └── signup/
└── (marketing)/ # Marketing pages
├── about/
└── pricing/
Parallel Routes
Use @slotName for parallel rendering:
app/
├── @sidebar/
│ └── page.tsx
├── @main/
│ └── page.tsx
└── layout.tsx # Receives both as props
Metadata & SEO
// Static metadata
export const metadata = {
title: 'Settings | Slotify',
description: 'Manage your booking preferences',
};
// Dynamic metadata
export async function generateMetadata({ params }) {
const { username } = await params;
return {
title: `Book with ${username} | Slotify`,
};
}
Loading & Error States
// app/bookings/loading.tsx
export default function Loading() {
return <BookingsSkeleton />;
}
// app/bookings/error.tsx
'use client';
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
Middleware
// middleware.ts (root level)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Auth check, redirects, etc.
}
export const config = {
matcher: ['/app/:path*'],
};
Caching & Revalidation
// Force dynamic (no cache)
export const dynamic = 'force-dynamic';
// Revalidate every 60 seconds
export const revalidate = 60;
// On-demand revalidation
import { revalidatePath, revalidateTag } from 'next/cache';
revalidatePath('/app/bookings');
revalidateTag('bookings');
Common Mistakes to Avoid
- ❌ Using
'use client'unnecessarily - ❌ Importing server-only code in client components
- ❌ Not awaiting
paramsin Next.js 16 - ❌ Missing loading/error states
- ❌ Not calling
revalidatePathafter mutations
Migration Notes (Next.js 15 → 16)
paramsis now async - mustawait paramssearchParamsis now async - mustawait searchParams- Update all dynamic route components accordingly
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon