Complete setup instructions for building a geometry education app with Next.js 15, TypeScript, Tailwind CSS, and interactive visualizations
Complete step-by-step instructions for building an interactive geometry education application with Next.js 15 App Router, TypeScript, Tailwind CSS, and Framer Motion.
Guides you through creating a fully-featured geometry tutor application with:
Create a new Next.js 15 project with TypeScript and App Router:
```bash
npx create-next-app@latest geometry-tutor
cd geometry-tutor
```
Select these options when prompted:
Install animation, state management, and UI libraries:
```bash
npm install framer-motion zustand @radix-ui/react-dialog @radix-ui/react-progress @radix-ui/react-tabs
npm install -D @tailwindcss/typography
```
**Key libraries:**
Generate the complete folder structure:
```bash
mkdir -p src/app/\(auth\)/login src/app/\(auth\)/register \
src/app/\(dashboard\)/progress src/app/\(dashboard\)/settings \
src/app/\(dashboard\)/modules \
src/components/ui src/components/modules \
src/components/layout src/components/shared \
src/lib src/hooks src/store \
public/images
```
**Structure breakdown:**
Update `tailwind.config.js` with custom theme:
```js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
darkMode: 'class',
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
secondary: {
500: '#8b5cf6',
600: '#7c3aed',
},
},
fontFamily: {
sans: ['var(--font-geist-sans)', 'system-ui', 'sans-serif'],
},
},
},
plugins: [require('@tailwindcss/typography')],
};
```
Create `src/lib/types.ts` with core data types:
```typescript
export interface Module {
id: string;
title: string;
description: string;
difficulty: 'beginner' | 'intermediate' | 'advanced';
estimatedTime: string;
topics: string[];
}
export interface UserProgress {
moduleId: string;
completedLessons: string[];
quizScores: Record<string, number>;
lastAccessed: Date;
}
export interface QuizQuestion {
id: string;
question: string;
options: string[];
correctAnswer: number;
explanation: string;
}
```
Create `src/lib/constants.ts` with module data:
```typescript
import { Module } from './types';
export const MODULES: Module[] = [
{
id: 'pythagorean-theorem',
title: 'Pythagorean Theorem',
description: 'Learn about right triangles and the relationship between their sides',
difficulty: 'beginner',
estimatedTime: '30 min',
topics: ['Right triangles', 'a² + b² = c²', 'Applications'],
},
{
id: 'triangle-properties',
title: 'Triangle Properties',
description: 'Explore types of triangles and their unique properties',
difficulty: 'beginner',
estimatedTime: '25 min',
topics: ['Types', 'Angles', 'Congruence'],
},
// Add more modules as needed
];
```
Create `src/store/user-progress-store.ts` for progress tracking:
```typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { UserProgress } from '@/lib/types';
interface ProgressStore {
progress: Record<string, UserProgress>;
completeLesson: (moduleId: string, lessonId: string) => void;
saveQuizScore: (moduleId: string, quizId: string, score: number) => void;
}
export const useProgressStore = create<ProgressStore>()(
persist(
(set) => ({
progress: {},
completeLesson: (moduleId, lessonId) =>
set((state) => ({
progress: {
...state.progress,
[moduleId]: {
...state.progress[moduleId],
completedLessons: [
...(state.progress[moduleId]?.completedLessons || []),
lessonId,
],
lastAccessed: new Date(),
},
},
})),
saveQuizScore: (moduleId, quizId, score) =>
set((state) => ({
progress: {
...state.progress,
[moduleId]: {
...state.progress[moduleId],
quizScores: {
...state.progress[moduleId]?.quizScores,
[quizId]: score,
},
lastAccessed: new Date(),
},
},
})),
}),
{ name: 'geometry-tutor-progress' }
)
);
```
Create `src/app/(dashboard)/layout.tsx`:
```typescript
import Link from 'next/link';
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="min-h-screen bg-gray-50">
<nav className="bg-white shadow-sm border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16 items-center">
<Link href="/" className="text-xl font-bold text-primary-600">
Geometry Tutor
</Link>
<div className="flex gap-6">
<Link href="/progress" className="hover:text-primary-600">
Progress
</Link>
<Link href="/settings" className="hover:text-primary-600">
Settings
</Link>
</div>
</div>
</div>
</nav>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{children}
</main>
</div>
);
}
```
Create `src/app/(dashboard)/page.tsx`:
```typescript
import { MODULES } from '@/lib/constants';
import Link from 'next/link';
export default function DashboardPage() {
return (
<div>
<h1 className="text-3xl font-bold mb-8">Your Geometry Modules</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{MODULES.map((module) => (
<Link
key={module.id}
href={`/modules/${module.id}`}
className="bg-white rounded-lg shadow hover:shadow-lg transition p-6"
>
<h3 className="text-xl font-semibold mb-2">{module.title}</h3>
<p className="text-gray-600 mb-4">{module.description}</p>
<div className="flex gap-2 text-sm text-gray-500">
<span className="bg-primary-50 px-2 py-1 rounded">
{module.difficulty}
</span>
<span className="bg-gray-100 px-2 py-1 rounded">
{module.estimatedTime}
</span>
</div>
</Link>
))}
</div>
</div>
);
}
```
Create `src/app/(dashboard)/modules/[moduleId]/page.tsx`:
```typescript
import { MODULES } from '@/lib/constants';
import Link from 'next/link';
import { notFound } from 'next/navigation';
export default async function ModulePage({
params,
}: {
params: Promise<{ moduleId: string }>;
}) {
const { moduleId } = await params;
const module = MODULES.find((m) => m.id === moduleId);
if (!module) {
notFound();
}
return (
<div>
<h1 className="text-3xl font-bold mb-4">{module.title}</h1>
<p className="text-gray-600 mb-8">{module.description}</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Link
href={`/modules/${moduleId}/lesson`}
className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition"
>
<h3 className="text-xl font-semibold mb-2">📖 Lesson</h3>
<p className="text-gray-600">Read the theory and concepts</p>
</Link>
<Link
href={`/modules/${moduleId}/demonstration`}
className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition"
>
<h3 className="text-xl font-semibold mb-2">🎨 Demonstration</h3>
<p className="text-gray-600">Interactive visual proof</p>
</Link>
<Link
href={`/modules/${moduleId}/quiz`}
className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition"
>
<h3 className="text-xl font-semibold mb-2">✅ Quiz</h3>
<p className="text-gray-600">Test your knowledge</p>
</Link>
<Link
href={`/modules/${moduleId}/review`}
className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition"
>
<h3 className="text-xl font-semibold mb-2">📝 Review</h3>
<p className="text-gray-600">Summary and key takeaways</p>
</Link>
</div>
</div>
);
}
```
Start the development server:
```bash
npm run dev
```
Open `http://localhost:3000` in your browser.
Deploy your app:
1. Push code to GitHub
2. Import repository on Vercel
3. Deploy with default settings (auto-detected Next.js)
**Component Organization:**
**State Management:**
**Animations:**
**Responsive Design:**
**Progressive Enhancement:**
**Issue:** Tailwind classes not applying
**Solution:** Ensure content paths in `tailwind.config.js` include all component directories
**Issue:** Zustand persist not working
**Solution:** Check that localStorage is available (not in SSR context)
**Issue:** Dynamic routes returning 404
**Solution:** Verify module IDs match exactly between constants and URL params
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/geometry-tutor-setup-guide/raw