A modern, responsive portfolio website built with Next.js 14, TypeScript, and Tailwind CSS. Features dynamic content management with Supabase integration, SEO optimization, and a clean, maintainable codebase.
git clone <repository-url>
cd portfolio-website
npm install
# or
yarn install
# or
pnpm install
.env.local file in the root directory:
```env
NEXT_PUBLIC_SUPABASE_URL=your-supabase-project-url NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
SUPABASE_URL=your-supabase-project-url SUPABASE_ANON_KEY=your-anon-key
**Important**: The `SERVICE_ROLE_KEY` is required on the server (API routes) for insert/update/delete operations. Never expose it to the browser.
4. Run the development server:
```bash
npm run dev
The application will be available at http://localhost:7000.
.
├── app/ # Next.js App Router pages
│ ├── about/ # About page
│ ├── api/ # API routes
│ │ └── links/ # Links API endpoints
│ ├── ess-api/ # ESS API documentation page
│ ├── experience/ # Experience page
│ ├── links/ # Links management page
│ ├── portfolio/ # Portfolio pages
│ │ ├── [id]/ # Dynamic portfolio detail page
│ │ └── page.tsx # Portfolio listing page
│ ├── fonts/ # Font files
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout
│ └── page.tsx # Home page
│
├── components/ # React components
│ ├── portfolio/ # Portfolio-specific components
│ ├── ui/ # Reusable UI components (shadcn/ui)
│ └── *.tsx # Feature components
│
├── docs/ # Documentation
│ └── ess-api-contract-be-laravel.yaml
│
├── hooks/ # Custom React hooks
│ └── use-mobile.tsx
│
├── lib/ # Utility libraries
│ ├── config/ # Configuration files
│ │ └── site-config.ts
│ ├── supabase/ # Supabase integration
│ │ ├── client.ts # Supabase client setup
│ │ └── data.ts # Data fetching functions
│ ├── utils/ # Utility functions
│ │ ├── canonical-url.ts
│ │ └── structured-data.ts
│ ├── data.ts # Local data fallback
│ ├── links-data.ts # Links data
│ └── utils.ts # General utilities
│
├── public/ # Static assets
│ ├── activity/ # Activity images
│ ├── client/ # Client logos
│ ├── cv/ # CV files
│ ├── experience/ # Experience logos
│ ├── portfolio/ # Portfolio images
│ ├── sertifikat/ # Certificates
│ ├── tech/ # Technology icons
│ └── robots.txt # Robots.txt file
│
├── scripts/ # Utility scripts
│ ├── database/ # Database migration scripts
│ │ └── migrate-to-supabase.ts
│ └── verification/ # Verification scripts
│ ├── verify-portfolio-images.ts
│ └── verify-unsplash-urls.ts
│
├── supabase/ # Supabase configuration
│ └── schema.sql # Database schema
│
├── types/ # TypeScript type definitions
│ └── global.d.ts
│
├── .env # Environment variables (not in git)
├── next.config.mjs # Next.js configuration
├── package.json # Dependencies and scripts
├── tailwind.config.ts # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
└── README.md # This file
app/: Next.js App Router directory containing all pages and API routescomponents/: React components organized by feature
portfolio/: Components specific to portfolio pagesui/: Reusable UI components from shadcn/uilib/: Utility libraries organized by purpose
config/: Site configurationsupabase/: Supabase client and data functionsutils/: General utility functionsscripts/: Utility scripts for maintenance
database/: Database migration and setup scriptsverification/: Scripts to verify data integritysupabase/schema.sqlThis will create the following tables:
portfolio_items - Portfolio projectswork_experiences - Work historyeducation_experiences - Education historyorganization_experiences - Organization historytechnologies - Technology stacklinks - External links (optional)If you want to use the links management feature, create a links table with these columns:
| Column | Type | Notes |
|---|---|---|
id |
text | primary key |
title |
text | |
url |
text | |
icon |
text | optional |
section |
text | optional group label |
preview_text |
text | optional preview content |
preview_domain |
text | optional preview domain |
preview_image |
text | optional image URL |
After setting up the database schema, migrate your data:
npm run migrate:supabase
This script will:
lib/data.tsNote: The app has a built-in fallback mechanism. If Supabase is not configured or unavailable, it will automatically fall back to lib/data.ts.
# Development
npm run dev # Start development server (port 7000)
# Production
npm run build # Build for production
npm run start # Start production server
# Maintenance
npm run lint # Run ESLint
npm run migrate:supabase # Migrate data to Supabase
npm run verify:portfolio-images # Verify portfolio images
npm run verify:unsplash-urls # Verify Unsplash URLs
All pages have canonical URLs pointing to the production domain (https://setiadyanwar.github.io) to prevent duplicate content issues.
Implementation:
metadataBase is set in app/layout.tsxalternates.canonical in its metadataThe next.config.mjs includes headers configuration to prevent indexing of Vercel subdomain:
async headers() {
return [
{
source: '/:path*',
has: [
{
type: 'host',
value: '*.vercel.app'
}
],
headers: [
{
key: 'X-Robots-Tag',
value: 'noindex'
}
]
}
];
}
To verify SEO implementation:
curl -s https://setiadyanwar.github.io | grep "canonical"
curl -I https://setiadyanwar-github-io.vercel.app
https://setiadyanwar.github.io/sitemap.xmlCheck if all portfolio images are present and valid:
npm run verify:portfolio-images
Check and fix broken Unsplash image URLs:
npm run verify:unsplash-urls
This script will:
The easiest way to deploy is using the Vercel Platform.
Make sure to set these in your deployment platform:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY (for API routes)lib/supabase/lib/config/lib/utils/Contributions are welcome! Please feel free to submit a Pull Request.
This project is private and proprietary.
Built with ❤️ using Next.js, TypeScript, and Tailwind CSS.