Skip to content

ColinRhys/vercel-portfolio-starter

Repository files navigation

vercel-portfolio-starter

A free, modern developer portfolio built with Next.js 16, TypeScript, and Tailwind CSS — deployed on Vercel for the same price as GitHub Pages, but with features GitHub Pages can't touch.


Why this exists

GitHub Pages is free and simple, but it's static. You can't run server-side logic, which means:

  • You can't have a contact form without exposing your email address
  • You can't add any backend features later

Vercel's Hobby tier is also free, supports custom domains, automatic SSL, and CI/CD from GitHub — but it also runs serverless functions. That's how this template handles the contact form without ever revealing your email.

Same price. Way more capable.


The killer feature: private contact form

Most portfolio contact forms either link to mailto:you@email.com (exposing your address to scrapers) or use a third-party service that puts their branding on your site.

This template uses Resend — a free email API — as a serverless function on Vercel. Here's what happens:

  1. Visitor fills out the form and hits Send
  2. The form submits to a Next.js Route Handler (/api/contact) running on Vercel's servers
  3. That function calls Resend with your API key — stored secretly as an environment variable, never visible in the browser
  4. Resend delivers the message to your inbox with the sender's email as a replyTo header
  5. You hit Reply — it goes straight to them
  6. Your email address is never in the code, never in the browser, never visible to anyone

Stack

Tool Purpose Cost
Next.js 16 Framework (App Router) Free
TypeScript Type safety Free
Tailwind CSS 4 Styling Free
Resend Contact form emails (3,000/month free) Free
Vercel Hobby Hosting, SSL, CI/CD, serverless functions Free
Custom domain From any registrar ~$10–15/year

Total annual cost: ~$10–15 (just the domain)


Real-world results

This template was deployed to Vercel and run through Google PageSpeed Insights to verify the claims. These are actual scores from the live deployment, not synthetic benchmarks.

Desktop

PageSpeed Desktop — 100 Performance, 96 Accessibility, 100 Best Practices, 100 SEO

Mobile

PageSpeed Mobile — 98 Performance, 96 Accessibility, 100 Best Practices, 100 SEO

Mobile Desktop
Performance 98 100
Accessibility 96 96
Best Practices 100 100
SEO 100 100
First Contentful Paint 0.8s 0.2s
Largest Contentful Paint 2.0s 0.4s

These scores are a direct result of how the template is built: no client-side data fetching, fonts self-hosted via next/font (eliminating the render-blocking Google Fonts CDN request), minimal JavaScript bundle, and server-rendered HTML on every request.


Quick start

1. Fork this repo

Click Fork in the top right on GitHub.

2. Customize your content

Edit data/profile.ts — this is the primary file you'll change to make it yours:

export const profile: Profile = {
  name: "Your Name",
  tagline: "Your Title",
  location: "Your City",
  bio: "A short bio about yourself.",
  description: "A one-line description for search engines and social previews.",
  linkedin: "https://linkedin.com/in/yourhandle",
  github: "https://github.com/yourhandle",
  siteUrl: "https://yourdomain.com",
  resumePath: "/resume.pdf",
};

export const experience: ExperienceItem[] = [
  {
    id: "your-role",
    company: "Your Company",
    title: "Your Title",
    date: "2022 — Present",
    description: "What you did there.",
    tags: ["TypeScript", "React"],
  },
  // add more...
];

3. Add your resume

Place your resume PDF at:

public/resume.pdf

The button in the Resume section links to /resume.pdf automatically. Keep the PDF free of contact details — the contact form handles that.

4. Set up Resend (free — takes 5 minutes)

  1. Create a free account at resend.com
  2. Go to API KeysCreate API Key → name it portfolio-prod
  3. Select Sending access, All Domains
  4. Copy the key — you only see it once

5. Deploy to Vercel

  1. Go to vercel.com and sign in with GitHub
  2. Click Add New Project → import your forked repo
  3. Vercel auto-detects Next.js — don't change any build settings
  4. Before deploying, expand Environment Variables and add:
Name Value
RESEND_API_KEY re_xxxxxxxxxxxx (from Resend)
CONTACT_EMAIL you@youremail.com (where you want messages delivered)
RESEND_FROM Portfolio Contact <onboarding@resend.dev> (see note below)

RESEND_FROM note: The default onboarding@resend.dev sender works immediately in Resend's sandbox but is limited to sending to your own verified email only. Once you've verified your own domain in Resend, update this to something like Portfolio Contact <hello@yourdomain.com> and it will work for all recipients.

  1. Click Deploy — your site will be live on a .vercel.app URL in ~60 seconds

6. Add your custom domain

In Vercel → your project → Domains → add your domain. Vercel shows you the DNS records to set at your registrar. SSL is automatic.

Once your domain is live, update siteUrl in data/profile.ts to your real URL — this feeds into the sitemap, SEO metadata, and social share previews.


Customization

Colors

All colors are CSS variables in app/globals.css:

@theme {
  --color-background: #f8f7f4;   /* page background */
  --color-ink: #1a1a2e;          /* primary text */
  --color-ink-muted: #4a4a6a;    /* secondary text */
  --color-accent: #d97706;       /* amber — change this to your color */
  --color-accent-light: #fef3c7; /* light tint of accent */
  --color-surface: #ffffff;      /* card backgrounds */
  --color-border: #e2e0da;       /* borders and dividers */
}

Change --color-accent and --color-accent-light to match your personal brand.

Fonts

Fonts are loaded via next/font/google in app/layout.tsx. To swap fonts:

  1. Replace Inter and Playfair_Display with any fonts from fonts.google.com
  2. Update the import and the two const declarations at the top of app/layout.tsx
  3. The CSS variables --font-sans and --font-display in app/globals.css will pick up the new fonts automatically — no changes needed there

Project structure

vercel-portfolio-starter/
├── app/
│   ├── api/
│   │   └── contact/
│   │       └── route.ts      # Serverless contact form handler
│   ├── globals.css            # Design tokens + Tailwind config
│   ├── layout.tsx             # Root layout + fonts + SEO metadata
│   └── page.tsx               # Home page
├── components/
│   ├── Hero.tsx               # Name, tagline, bio, nav links
│   ├── Experience.tsx         # Work history list
│   ├── Resume.tsx             # Resume download button
│   ├── Contact.tsx            # Contact form (client component)
│   └── Footer.tsx             # Footer with social links
├── data/
│   └── profile.ts             # ← Edit this file to customize content
└── public/
    └── resume.pdf             # ← Drop your resume PDF here

Adding content

All content lives in data/profile.ts. To add a new experience entry, add an object to the experience array. No code changes required elsewhere — the component reads the data automatically.


OG image (social share preview)

Social platforms (LinkedIn, Twitter/X, Slack) display a preview card when your URL is shared. The card pulls from the OG image metadata in app/layout.tsx, which points to {siteUrl}/og-image.png.

An OG image is not included in this template because it should contain your name, title, and personal branding — it can't be meaningfully pre-made for someone else. OG images also require an absolute public URL to your deployed domain, so they can't be tested until the site is live anyway.

To add one (recommended):

  1. Create a 1200×630 PNG with your name and title — tools like Figma, Canva, or og-image.vercel.app make this straightforward
  2. Save it to public/og-image.png
  3. Make sure siteUrl in data/profile.ts is set to your real domain

Once deployed, you can verify your card at cards-dev.twitter.com/validator or LinkedIn's Post Inspector.


License

MIT — use it, fork it, make it your own.


Built by Colin Rhys

About

Starter template for a portfolio site

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors