Frequently Asked Questions (FAQ)¶
Breadcrumbs: Documentation > Guides > FAQ
Answers to common questions about the portfolio website architecture, development, and deployment.
Table of Contents¶
- General Questions
- Development Questions
- Content Management
- Deployment Questions
- Performance Questions
- SEO Questions
- Customization Questions
General Questions¶
What technology stack does this portfolio use?¶
Stack:
- Framework: Next.js 15 with App Router
- Language: TypeScript 5
- Styling: Tailwind CSS 4
- UI Components: shadcn/ui (Radix UI + Tailwind)
- Content: JSON files
- Deployment: Docker + Docker Compose
- Testing: Jest with React Testing Library
Why these choices?
- Next.js 15 for modern React with built-in SSR/SSG
- TypeScript for type safety and better DX
- Tailwind CSS for rapid, consistent styling
- shadcn/ui for accessible, customizable components
- JSON for simple, git-friendly content management
Is this portfolio open source?¶
Yes! The code is available on GitHub. You can:
- View the source code
- Fork for your own portfolio
- Contribute improvements
- Report issues
License: Check the repository for license information.
Can I use this as a template for my portfolio?¶
Yes! This is designed as a reusable portfolio template. To use it:
- Fork the repository
- Update
lib/config.tswith your information - Replace content in
content/directories - Update images in
public/images/ - Customize styling in
tailwind.config.tsandapp/globals.css - Deploy to your hosting platform
See Contributing Guide for setup instructions.
What's the difference between v1 and v2?¶
v1 (Legacy):
- Next.js 12 with Pages Router
- Single JSON file for projects
- Basic SEO
- Older dependency versions
v2 (Current):
- Next.js 15 with App Router
- Individual JSON files per project
- Advanced SEO with metadata API
- Structured data (JSON-LD)
- LLM discovery endpoint (/llms.txt)
- Improved performance
- Modern tooling (Turbopack, Tailwind v4)
See Migration Guide for upgrading.
Development Questions¶
Why do I need Node.js 20+?¶
Next.js 15 requires Node.js 20 or higher for:
- Modern JavaScript features (ES2023+)
- Better performance
- Native fetch API support
- Improved module resolution
Check your version:
Do I need to know Next.js to contribute?¶
Basic knowledge helps, but not required:
Need to know:
- React fundamentals (components, props, hooks)
- TypeScript basics (types, interfaces)
- Basic Git workflow
Nice to have:
- Next.js App Router concepts
- Server vs Client Components
- Tailwind CSS
- Testing with Jest
Learning resources:
What's the difference between server and client components?¶
Server Components (default):
// app/projects/page.tsx - No 'use client' needed
export default async function ProjectsPage() {
const projects = await getProjects(); // Runs on server
return <div>{/* ... */}</div>;
}
Benefits:
- Fetch data on server
- Reduce JavaScript bundle size
- Better SEO
- Access server-only APIs
Client Components:
// components/theme-toggle.tsx
'use client'; // Required directive
export function ThemeToggle() {
const [theme, setTheme] = useState('light'); // Hooks require client
return <button onClick={() => setTheme('dark')}>Toggle</button>;
}
Use when you need:
- React hooks (useState, useEffect, etc.)
- Event handlers (onClick, onChange, etc.)
- Browser APIs (window, document, etc.)
- Real-time interactivity
Why are absolute imports (@/) used instead of relative paths?¶
Benefits:
- Cleaner imports
- Easier refactoring
- Avoid
../../..chains - Consistent across codebase
// ❌ Relative imports - hard to maintain
import { Button } from "../../../components/ui/button";
import { cn } from "../../lib/utils";
// ✅ Absolute imports - clean and clear
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
Configuration:
How do I add a new page?¶
Steps:
- Create page file in
app/directory:
// app/my-page/page.tsx
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My Page',
description: 'Description of my page',
};
export default function MyPage() {
return (
<div className="container mx-auto py-12">
<h1 className="text-4xl font-bold">My Page</h1>
<p>Page content goes here</p>
</div>
);
}
- Add to navigation (if needed):
// components/layout/header.tsx
const navItems = [
{ href: "/", label: "Home" },
{ href: "/projects", label: "Projects" },
{ href: "/my-page", label: "My Page" }, // Add here
];
- Test locally:
How do I run tests?¶
# Run all tests once
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm test -- --coverage
# Run specific test file
npm test -- project-card.test.tsx
Test file location:
- Place tests next to components:
components/__tests__/ - Or in a separate
__tests__directory - Name:
*.test.tsor*.test.tsx
Content Management¶
How do I add a new project?¶
Step-by-step:
- Create JSON file in
content/projects/:
- Add project data:
{
"title": "My New Project",
"shortDescription": "Brief one-line description",
"description": "Detailed description explaining what the project does, technologies used, and key features.",
"technologies": ["Next.js", "TypeScript", "Tailwind CSS"],
"images": [
{
"src": "/images/projects/my-new-project/screenshot.jpg",
"alt": "Main dashboard screenshot"
}
],
"demoUrl": "https://demo.example.com",
"githubUrl": "https://github.com/username/repo",
"order": 7
}
- Add images:
- Test:
See Content Guide for details.
What image formats are supported?¶
Supported formats:
- Images:
.jpg,.jpeg,.png,.webp,.gif - Videos:
.mp4,.webm,.mov
Recommendations:
- Use WebP for best compression
- Max width: 1920px
- Optimize before adding (use tools like TinyPNG)
- Use descriptive alt text
Detecting videos:
import { isVideoFile } from "@/lib/utils";
const media = project.images.filter((img) => !isVideoFile(img.src));
const videos = project.images.filter((img) => isVideoFile(img.src));
Can I use Markdown instead of JSON?¶
Currently, content is JSON-based for simplicity. To add Markdown support:
- Install dependencies:
- Create MDX loader:
// lib/markdown.ts
import fs from "fs";
import path from "path";
import matter from "gray-matter";
export function getProjectFromMarkdown(slug: string) {
const filePath = path.join(process.cwd(), "content/projects", `${slug}.md`);
const fileContent = fs.readFileSync(filePath, "utf8");
const { data, content } = matter(fileContent);
return {
slug,
...data,
content,
};
}
- Update project loader to support both JSON and Markdown.
Note: This requires additional implementation. Open an issue if you need this feature.
How do I delete a project?¶
- Delete JSON file:
- Delete images:
- Restart dev server:
Project will no longer appear on the site.
What is the order field for?¶
The order field controls:
- Featured projects - Projects with
order: 1-6appear on the homepage - Sort order - Lower numbers appear first
{
"order": 1 // Highest priority, appears first
}
{
"order": 6 // Still featured, appears sixth
}
{
// No order field = not featured, sorted alphabetically
}
Featured projects section:
// lib/projects.ts
export async function getFeaturedProjects() {
const projects = await getProjects();
return projects.filter((p) => p.order && p.order >= 1 && p.order <= 6);
}
Deployment Questions¶
How do I deploy this portfolio?¶
Options:
- Vercel (Recommended):
- Docker:
- Other platforms: Netlify, AWS, Railway, etc.
See Deployment Guide for details.
Why use Docker?¶
Benefits:
- Consistent environment (dev = prod)
- Easy deployment
- Isolated dependencies
- Portable across systems
- Simple scaling
When to use Docker:
- Self-hosted deployments
- VPS hosting
- Multiple environments
When not needed:
- Deploying to Vercel/Netlify (they handle builds)
- Simple static hosting
What environment variables are required?¶
Minimum required:
Recommended:
# Site info
NEXT_PUBLIC_SITE_NAME="Your Name"
NEXT_PUBLIC_SITE_URL="https://your-domain.com"
NEXT_PUBLIC_SITE_DESCRIPTION="Your description"
# Author
NEXT_PUBLIC_AUTHOR_NAME="Your Name"
NEXT_PUBLIC_AUTHOR_EMAIL="your-email@example.com"
# Social links
NEXT_PUBLIC_GITHUB_URL="https://github.com/username"
NEXT_PUBLIC_LINKEDIN_URL="https://linkedin.com/in/username"
Optional:
# Analytics
NEXT_PUBLIC_GA_ID="G-XXXXXXXXXX"
NEXT_PUBLIC_GTM_ID="GTM-XXXXXXX"
# Webhook
WEBHOOK_URL="https://your-webhook.com"
WEBHOOK_ENABLED="true"
See Configuration Guide for all variables.
How do I set up a custom domain?¶
For Vercel:
- Go to project settings → Domains
- Add your domain (e.g.,
your-domain.com) - Update DNS records (provided by Vercel)
- Wait for DNS propagation (5-60 minutes)
For Docker/VPS:
- Point domain A record to server IP
- Set up reverse proxy (Nginx, Caddy, or Traefik)
- Configure SSL/TLS (Let's Encrypt)
- Update environment variables
Performance Questions¶
How can I improve page load speed?¶
Checklist:
- Optimize images:
import Image from 'next/image';
<Image
src="/image.jpg"
width={800}
height={600}
priority // For above-fold images
placeholder="blur"
/>;
- Lazy load components:
import dynamic from 'next/dynamic';
const Gallery = dynamic(() => import('@/components/gallery'), {
loading: () => <p>Loading...</p>,
});
- Enable caching:
// app/api/route.ts
export async function GET() {
return new Response(data, {
headers: {
"Cache-Control": "public, max-age=86400",
},
});
}
- Analyze bundle size:
See Performance Guide for more tips.
Why is my build slow?¶
Common causes:
- Too many images - Optimize images before adding
- Large dependencies - Review
package.json - Low memory - Increase Node.js heap size
- Many pages - Normal for sites with 100+ pages
Benchmarks:
- Small portfolio (< 20 pages): 30-60 seconds
- Medium portfolio (20-50 pages): 1-2 minutes
- Large portfolio (50+ pages): 2-5 minutes
SEO Questions¶
How is SEO handled?¶
Built-in SEO features:
- Metadata API:
export const metadata: Metadata = {
title: 'Page Title',
description: 'Page description',
openGraph: { ... },
twitter: { ... },
};
- Dynamic sitemap:
/sitemap.xml - Robots.txt:
/robots.txt - Structured data: JSON-LD schemas
- LLM discovery:
/llms.txt
See SEO Guide for details.
What is /llms.txt?¶
An AI agent discovery endpoint that provides structured information about the portfolio for LLMs (Language Learning Models) like ChatGPT, Claude, etc.
Purpose:
- Help AI agents recommend you for jobs
- Provide structured data about skills, projects, experience
- Improve discoverability by AI tools
Example usage:
- User: "Who's a good Next.js developer in Belgium?"
- AI: Reads
/llms.txt, sees your info, recommends you
Implementation:
// app/llms.txt/route.ts
export async function GET() {
const content = generateLLMSummary();
return new Response(content, {
headers: { "Content-Type": "text/plain" },
});
}
See LLMs.txt Guide for details.
How do I improve search ranking?¶
Best practices:
- Quality content: Well-written project descriptions
- Unique titles: Descriptive, keyword-rich titles
- Alt text: Descriptive alt text for all images
- Mobile-friendly: Responsive design (built-in)
- Fast loading: Optimize images, lazy loading
- Regular updates: Add new projects, keep content fresh
- Backlinks: Share your portfolio on social media, forums
Tools:
- Google Search Console
- Google PageSpeed Insights
- Lighthouse (Chrome DevTools)
Customization Questions¶
How do I change colors/theme?¶
Edit CSS variables:
/* app/globals.css */
:root {
--primary: oklch(0.48 0.0813 198.19);
--accent: #ffb900;
/* ... other colors ... */
}
.dark {
--primary: oklch(0.68 0.13 264.2);
--accent: #ffd600;
/* ... dark mode colors ... */
}
Or use Tailwind config:
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
primary: "hsl(var(--primary))",
accent: "hsl(var(--accent))",
},
},
},
};
See Design System Guide.
How do I add new sections to the homepage?¶
- Create component:
// components/sections/testimonials.tsx
export function TestimonialsSection() {
return (
<section className="py-24">
<div className="container mx-auto">
<h2 className="text-3xl font-bold mb-8">Testimonials</h2>
{/* Content */}
</div>
</section>
);
}
- Add to homepage:
// app/page.tsx
import { TestimonialsSection } from '@/components/sections/testimonials';
export default function HomePage() {
return (
<>
<HeroSection />
<ProjectsSection />
<TestimonialsSection /> {/* Add here */}
<ContactSection />
</>
);
}
Can I change the layout/design completely?¶
Yes! The architecture supports complete customization:
- Update components in
components/ - Modify styles in
app/globals.cssand Tailwind config - Change layout in
app/layout.tsx - Add new pages in
app/
The data layer (lib/, content/) is separate, so you can rebuild the UI without changing content structure.
See Also¶
- Contributing Guide - Development workflow
- Troubleshooting - Common issues
- Best Practices - Code patterns
- Architecture Overview - Project structure
Still Have Questions?¶
- Check GitHub Discussions
- Open an issue
- Review the documentation
Last Updated: February 2026
Maintainers: Simon Stijnen
Questions? Open an issue on GitHub