Next.js 14 + React 18 + TypeScript educational web portal with Tailwind CSS.
npm run dev # Start dev server (http://localhost:3000)
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint (requires initial setup)No test framework is configured yet. To add tests, install Jest + React Testing Library.
- Use
@/alias for internal imports (e.g.,@/app/components/TopicChecklist) - Client components must start with
"use client";directive - Group imports: React imports → external libs → internal imports
- Sort imports alphabetically within groups
- Strict mode enabled in
tsconfig.json - Always define prop types using interfaces/type aliases
- Avoid
any- useunknownwhen type is uncertain - Use
Record<K, V>for dictionary-like objects
- Components: PascalCase (e.g.,
TopicChecklist) - Functions/variables: camelCase (e.g.,
toggleTopic,completed) - Files: kebab-case (e.g.,
topic-checklist.tsx) - CSS classes: Prefix with
fc-(e.g.,fc-section,fc-topicItem)
"use client";
import { useState, useEffect } from "react";
type Props = {
// define props
};
export default function ComponentName({ prop1 }: Props) {
// hooks first
// state
// handlers
// render
}- Wrap
JSON.parse()in try/catch - Always handle localStorage errors gracefully
- Use optional chaining (
?.) and nullish coalescing (??) - Provide fallback values for potentially undefined state
- Uses global CSS in
src/app/globals.css - All custom classes use
fc-prefix (functional component) - Tailwind utilities available for rapid styling
src/app/
├── components/ # Reusable components
├── data/ # Static data (topics.ts)
├── lib/ # Utilities (storage.ts)
├── topic/[id]/ # Dynamic route pages
├── layout.tsx # Root layout
├── page.tsx # Home page
└── globals.css # Global styles
None found. Project uses standard Next.js + ESLint configuration.
// src/app/topic/[id]/page.tsx
type TopicPageProps = {
params: {
id: string;
};
};
export default function TopicPage({ params }: TopicPageProps) {
const block = topicBlocks.find((item) => item.slug === params.id);
// ...
}Use the storage utilities from @/app/lib/storage:
import { getProgress, setProgress, getNotes, setNotes } from "@/app/lib/storage";// src/app/data/topics.ts
export type Topic = {
id: string;
title: string;
shortDescription: string;
explanation: string;
example: string;
glossary: GlossaryItem[];
difficulty?: "easy" | "medium" | "hard";
tags?: string[];
};
export type TopicBlock = {
id: string;
slug: string;
emoji: string;
title: string;
description: string;
level: string;
topics: Topic[];
};- Hydration handling: Always check
isHydratedbefore accessing browser APIs - localStorage: Use try/catch for all localStorage operations
- Accessibility: Include proper
typeattributes on buttons,htmlForon labels - Keys: Always provide stable keys when mapping arrays
- Null checks: Validate dynamic route params before rendering
The project needs ESLint configuration. Run npm run lint and select "Strict" when prompted, or create .eslintrc.json manually:
{
"extends": "next/core-web-vitals"
}The project uses these localStorage keys:
web-coding-progress- completed topicsweb-coding-notes- notes by topicweb-coding-note-timestamps- timestamps for notesweb-coding-last-opened- last opened block slugweb-coding-favorites- favorite topicsweb-coding-theme- theme preference
All storage operations should go through @/app/lib/storage for consistency and easy migration to cloud storage later.