Directory Structure¶
Navigation: Documentation Home → Architecture → Directory Structure
Table of Contents¶
Overview¶
The project follows Next.js 15 App Router conventions with a clear separation of concerns. All application code lives in dedicated directories, while configuration files reside at the root level.
Directory Tree¶
/workspaces/website/
├── app/ # Next.js App Router - pages and layouts
│ ├── projects/ # Projects section routes
│ │ ├── [slug]/ # Dynamic project detail pages
│ │ │ └── page.tsx # Individual project page
│ │ └── page.tsx # Projects listing page
│ ├── llms.txt/ # LLM-readable site information
│ ├── layout.tsx # Root layout (global)
│ ├── page.tsx # Home page
│ ├── error.tsx # Global error boundary
│ ├── not-found.tsx # 404 page
│ └── globals.css # Global styles
│
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── badge.tsx
│ │ ├── dialog.tsx
│ │ ├── carousel.tsx
│ │ ├── table.tsx
│ │ └── ... # Other UI primitives
│ ├── layout/ # Layout components
│ │ ├── header.tsx # Site header with navigation
│ │ └── footer.tsx # Site footer
│ ├── meta/ # SEO and analytics
│ │ ├── analytics.tsx # Google Analytics component
│ │ ├── structured-data.tsx # Schema.org Person data
│ │ └── project-structured-data.tsx # Schema.org Project data
│ ├── icons/ # Custom icon components
│ │ ├── github-icon.tsx
│ │ ├── linkedin-icon.tsx
│ │ └── file-download-icon.tsx
│ ├── project-card.tsx # Project card component
│ ├── achievement-card.tsx # Achievement card component
│ ├── skills-data-table.tsx # Sortable skills table
│ ├── related-projects.tsx # Related projects section
│ ├── timeline.tsx # Timeline for experience
│ ├── social-link.tsx # Social media link
│ ├── theme-toggle.tsx # Dark/light mode toggle
│ ├── badge-overflow.tsx # Badge list with overflow
│ ├── email-copy-button.tsx # Email copy to clipboard
│ └── copyright.tsx # Copyright notice
│
├── lib/ # Utilities and data access
│ ├── projects.ts # Project data access functions
│ ├── achievements.ts # Achievement data access functions
│ ├── skills.ts # Skills generation from projects
│ ├── config.ts # Environment configuration
│ ├── utils.ts # Utility functions (cn, isVideoFile)
│ └── file-download.ts # File download utility
│
├── content/ # Content storage (JSON)
│ ├── projects/ # Project data files
│ │ ├── audionome.json
│ │ ├── bemed.json
│ │ ├── cerm-mcp-poc.json
│ │ └── ... # One JSON file per project
│ └── achievements/ # Achievement data files
│ ├── rotary-award.json
│ └── ... # One JSON file per achievement
│
├── public/ # Static assets
│ ├── images/ # Image assets
│ │ ├── projects/ # Project images
│ │ │ ├── audionome/ # Organized by project slug
│ │ │ │ ├── preview.jpg
│ │ │ │ └── quick-demo.webp
│ │ │ └── ...
│ │ ├── logos/ # Company/school logos
│ │ ├── hero.jpg # Hero image
│ │ └── profile-meta.jpg # OpenGraph image
│ ├── download/ # Downloadable files
│ │ └── resume.pdf
│ ├── favicon.ico
│ ├── site.webmanifest
│ └── robots.txt
│
├── middleware/ # Next.js middleware
│ └── analytics-webhook.ts # Analytics tracking middleware
│
├── tests/ # Jest unit tests
│ ├── app/ # Page component tests
│ ├── components/ # Component tests
│ ├── lib/ # Library function tests
│ ├── middleware/ # Middleware tests
│ └── utils/ # Utility tests
│
├── docs/ # Documentation (this file!)
│ ├── 01-architecture/ # Architecture documentation
│ ├── 02-features/ # Features and components
│ ├── 03-content/ # Content management
│ ├── 04-development/ # Development guides
│ ├── 05-deployment/ # Deployment guides
│ ├── 06-guides/ # Additional guides and references
│ └── README.md # Documentation home
│
├── .github/ # GitHub configuration
│ ├── workflows/ # GitHub Actions CI/CD
│ │ └── ci.yml # Continuous integration
│ └── copilot-instructions.md # AI agent instructions
│
├── .husky/ # Git hooks
│ ├── _/ # Husky setup
│ └── pre-commit # Pre-commit hook
│
├── .devcontainer/ # VS Code dev container
│ └── devcontainer.json
│
├── .vscode/ # VS Code settings
│ └── settings.json
│
├── next.config.ts # Next.js configuration
├── tsconfig.json # TypeScript configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── eslint.config.mjs # ESLint configuration
├── .prettierrc.json # Prettier configuration
├── jest.config.js # Jest configuration
├── jest.setup.js # Jest setup
├── package.json # Dependencies and scripts
├── package-lock.json # Dependency lock file
├── docker-compose.yml # Docker orchestration
├── Dockerfile # Docker image definition
├── .env # Environment variables (not in git)
├── .gitignore # Git ignore rules
├── README.md # Project README
└── AGENTS.md # AI agent instructions
Root Level¶
Configuration Files¶
The root directory contains all configuration files for the project's tools and frameworks.
| File | Purpose | See Also |
|---|---|---|
package.json |
Dependencies, scripts, lint-staged config | Configuration Reference |
next.config.ts |
Next.js framework settings | Configuration Reference |
tsconfig.json |
TypeScript compiler options | Configuration Reference |
tailwind.config.ts |
Tailwind CSS customization | Configuration Reference |
eslint.config.mjs |
ESLint linting rules | Configuration Reference |
.prettierrc.json |
Prettier formatting rules | Configuration Reference |
jest.config.js |
Jest testing configuration | Configuration Reference |
docker-compose.yml |
Docker multi-container setup | Deployment Guide |
Dockerfile |
Docker image definition | Deployment Guide |
Documentation Files¶
| File | Purpose |
|---|---|
README.md |
Project overview and quick start |
AGENTS.md |
Instructions for AI coding agents |
.github/copilot-instructions.md |
GitHub Copilot guidance |
Application Code¶
app/ Directory¶
The app/ directory uses Next.js 15 App Router conventions. Each folder can contain:
page.tsx- Page component (required for route)layout.tsx- Layout wrapper for nested routeserror.tsx- Error boundaryloading.tsx- Loading fallback (optional, not currently used)not-found.tsx- 404 fallback
Route Structure¶
app/
├── layout.tsx → All pages
├── page.tsx → / (home)
├── projects/
│ ├── page.tsx → /projects (listing)
│ └── [slug]/
│ └── page.tsx → /projects/[slug] (dynamic)
Example: Dynamic Route Handler
```20:27:/workspaces/website/app/projects/[slug]/page.tsx // Generate static params for all projects at build time export async function generateStaticParams() { const projects = await getProjects();
return projects.map((project) => ({ slug: project.slug, })); }
See [Routing & Navigation](./04-routing-navigation.md) for detailed routing documentation.
### `components/` Directory
React components are organized by category:
#### UI Components (`components/ui/`)
shadcn/ui components installed via CLI. These are:
- **Owned by the project** (not npm packages)
- **Customizable** - modify freely to fit project needs
- **Styled with Tailwind** - New York style, Slate base color
**Installation Example:**
```bash
npx shadcn@latest add button card badge
Component Example:
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"
export function MyComponent() {
return (
<Card>
<CardHeader>Title</CardHeader>
<CardContent>
<Button variant="secondary">Click me</Button>
</CardContent>
</Card>
)
}
Layout Components (components/layout/)¶
header.tsx- Site navigation with theme togglefooter.tsx- Footer with links and copyright
Meta Components (components/meta/)¶
SEO and analytics:
analytics.tsx- Google Analytics and GTM integrationstructured-data.tsx- Schema.org Person structured dataproject-structured-data.tsx- Schema.org Project structured data
Custom Components¶
Domain-specific components:
project-card.tsx- Project preview cardachievement-card.tsx- Achievement display cardskills-data-table.tsx- Interactive skills table with @tanstack/react-tablerelated-projects.tsx- Related projects based on shared technologiestimeline.tsx- Timeline for work/education experience
lib/ Directory¶
Server-side utilities and data access functions. All functions are async and run on the server.
Data Access Functions¶
```9:19:/workspaces/website/lib/projects.ts export interface Project { slug: string; title: string; shortDescription: string; description: string; technologies: string[]; images: ProjectImage[]; demoUrl?: string; githubUrl?: string; order?: number; }
**Key Functions:**
- `getProjects()` - Get all projects
- `getFeaturedProjects()` - Get featured projects (order 1-6)
- `getProjectBySlug(slug)` - Get single project
- `getAchievements()` - Get all achievements
- `getSkills()` - Generate skills from projects
See [Data Flow](./03-data-flow.md) for detailed data access patterns.
#### Utility Functions (`lib/utils.ts`)
```typescript
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
// Merge Tailwind classes with conflict resolution
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Check if file is a video
export function isVideoFile(src: string): boolean {
const videoExtensions = [".mp4", ".webm", ".ogg", ".mov"];
return videoExtensions.some((ext) => src.toLowerCase().endsWith(ext));
}
Configuration (lib/config.ts)¶
Centralized environment variable access:
```9:29:/workspaces/website/lib/config.ts // Site configuration export const siteConfig = { name: process.env.NEXT_PUBLIC_SITE_NAME || "Simon Stijnen", url: process.env.NEXT_PUBLIC_SITE_URL || "https://simon.stijnen.be", description: process.env.NEXT_PUBLIC_SITE_DESCRIPTION || "Software engineer and AI student in Belgium building scalable, reliable products.", recruiterSummary: process.env.NEXT_PUBLIC_RECRUITER_SUMMARY || "Software engineer and AI student focused on scalable web apps, LLM automation, and data-driven systems. Available for internships and junior roles.", author: { name: process.env.NEXT_PUBLIC_AUTHOR_NAME || "Simon Stijnen", email: process.env.NEXT_PUBLIC_AUTHOR_EMAIL || "simon.stijnen.23+portfolio@gmail.com", }, location: { city: process.env.NEXT_PUBLIC_AUTHOR_CITY || "Bruges", country: process.env.NEXT_PUBLIC_AUTHOR_COUNTRY || "Belgium", }, social: { github: process.env.NEXT_PUBLIC_GITHUB_URL || "https://github.com/SimonStnn",
### `content/` Directory
JSON files store all content. Each file represents one piece of content (project or achievement).
**Naming Convention:** Use kebab-case matching the slug
- `audionome.json` → slug: `"audionome"` → route: `/projects/audionome`
- `cerm-mcp-poc.json` → slug: `"cerm-mcp-poc"` → route: `/projects/cerm-mcp-poc`
**Project JSON Structure:**
```1:28:/workspaces/website/content/projects/audionome.json
{
"title": "Audionome: Music Genre Classification",
"shortDescription": "Using a Support Vector Machine (SVM) to classify music clips based on their genre.",
"description": "For the AI Machine Learning course at VIVES University of Applied Sciences, I worked with Lynn Delaere on Audionome: an AI-powered system for music genre classification.\nWe trained several models (including logistic regression, SGD, and random forest) to automatically recognize and accurately classify music clips based on their genre. The project combines audio processing, machine learning, and a user-friendly interface built with Streamlit.",
"technologies": [
"AI",
"SVM",
"Streamlit",
"Python",
"Pandas",
"NumPy",
"Librosa",
"Scikit-learn"
],
"images": [
{
"src": "/images/projects/audionome/preview.jpg",
"alt": "Audionome application interface showing music genre classification"
},
{
"src": "/images/projects/audionome/quick-demo.webp",
"alt": "Demo screenshot of Audionome classifying a music track with SVM model results"
}
],
"demoUrl": "https://audionome.streamlit.app/",
"githubUrl": "https://github.com/SimonStnn/Audionome"
}
See Adding New Projects for content creation guide.
public/ Directory¶
Static assets served directly by Next.js. Files in public/ are available at the root URL.
Directory Organization:
public/
├── images/
│ ├── projects/ # Project images
│ │ └── {slug}/ # One folder per project
│ ├── logos/ # Company/school logos
│ ├── hero.jpg # Hero section image
│ └── profile-meta.jpg # OpenGraph share image
├── download/
│ └── resume.pdf # Downloadable resume
├── favicon.ico
├── site.webmanifest
└── robots.txt
Image Path Convention:
- Project images:
/images/projects/{slug}/image.jpg - Logos:
/images/logos/company.webp - Static images:
/images/hero.jpg
Example Usage:
<Image
src="/images/projects/audionome/preview.jpg"
alt="Audionome preview"
width={1200}
height={675}
/>
tests/ Directory¶
Jest unit tests mirror the source structure:
tests/
├── app/ # Page tests
│ ├── page.test.tsx
│ └── projects/
│ └── [slug]/
│ └── page.test.tsx
├── components/ # Component tests
│ ├── project-card.test.tsx
│ └── ui/
│ └── button.test.tsx
├── lib/ # Library tests
│ ├── projects.test.ts
│ └── skills.test.ts
└── middleware/ # Middleware tests
└── analytics-webhook.test.ts
Test Naming Convention:
{filename}.test.tsfor utilities{filename}.test.tsxfor components
Running Tests:
Configuration Files¶
TypeScript Configuration¶
Path aliases are configured in tsconfig.json:
```20:24:/workspaces/website/tsconfig.json "paths": { "@/": ["./"] } }, "include": ["next-env.d.ts", "/*.ts", "/.tsx", ".next/types//.ts"],
**Usage:**
```typescript
// Instead of: import { Button } from "../../components/ui/button"
import { Button } from "@/components/ui/button";
See Conventions for import standards.
Tailwind Configuration¶
```1:19:/workspaces/website/tailwind.config.ts import type { Config } from "tailwindcss";
const config = { darkMode: "class", content: [ "./pages//*.{ts,tsx}", "./components//.{ts,tsx}", "./app//.{ts,tsx}", "./src/*/.{ts,tsx}", ], theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, extend: {
**Key Features:**
- Dark mode via `class` strategy
- Custom HSL color variables
- shadcn/ui design tokens
- Container responsive utilities
### ESLint & Prettier
**ESLint extends:**
- `next/core-web-vitals` - Next.js best practices
- `next/typescript` - TypeScript rules
- `prettier` - Disable conflicting rules
**Prettier configuration:**
```1:9:/workspaces/website/.prettierrc.json
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"plugins": ["prettier-plugin-tailwindcss"]
}
Key Settings:
- Double quotes for consistency
- 2-space indentation
- 100 character line width
- Automatic Tailwind class sorting
Directory Reference¶
Quick Reference Table¶
| Directory | Purpose | Import Path | Content Type |
|---|---|---|---|
app/ |
Pages & layouts | Direct route | .tsx files |
components/ |
React components | @/components/* |
.tsx files |
components/ui/ |
shadcn/ui primitives | @/components/ui/* |
.tsx files |
lib/ |
Utilities & data | @/lib/* |
.ts files |
content/ |
JSON data | Server-side only | .json files |
public/ |
Static assets | /path/to/file |
Images, files |
tests/ |
Unit tests | N/A | .test.ts(x) |
docs/ |
Documentation | N/A | .md files |
File Naming Conventions¶
| Type | Convention | Example |
|---|---|---|
| Pages | page.tsx |
app/page.tsx |
| Layouts | layout.tsx |
app/layout.tsx |
| Components | PascalCase | ProjectCard.tsx or project-card.tsx |
| Utilities | kebab-case | file-download.ts |
| Content | kebab-case (slug) | audionome.json |
| Tests | {name}.test.ts(x) |
projects.test.ts |
| Types | PascalCase interface | export interface Project |
See Conventions for detailed naming standards.
See Also¶
Related documentation:
- Architecture Overview - High-level architecture and tech stack
- Data Flow - How data moves through directories
- Routing & Navigation - How
app/directory maps to routes - Conventions - File naming and code standards
- Configuration Reference - Detailed config file documentation
Next Steps¶
- Understand routing: Read Routing & Navigation to see how
app/becomes routes - Learn data flow: Study Data Flow to understand content → component pipeline
- Add content: Follow Adding New Projects to create content
- Explore components: Review Component Reference for component API