Building a Modern Portfolio Website with Cloudflare Pages & Vanilla JS
Your portfolio website is your digital storefront. Whether you're a freelancer, developer, designer, or business owner, a fast, well-designed portfolio can make the difference between landing a client and losing them to a competitor.
In this guide, we'll walk through building a complete portfolio website using nothing but vanilla HTML, CSS, and JavaScript — no frameworks, no build tools, no complicated setup. And we'll deploy it on Cloudflare Pages for free, with HTTPS, custom domain, and global CDN baked in.
Why Vanilla JS + Cloudflare Pages?
Modern web development often feels like a maze of frameworks, bundlers, and config files. But for a portfolio website, simplicity wins. Here's why this stack makes sense:
- Zero build step — Write code, push to Git, and it's live. No npm install, no webpack, no waiting.
- Blazing fast — Cloudflare Pages serves static files from its global CDN. No server-side rendering delays.
- Free hosting — Cloudflare Pages offers generous free tier with unlimited bandwidth for static sites.
- SEO-friendly — Static HTML is instantly crawlable by search engines.
- Full control — Every line of code is yours. No framework lock-in, no bloat.
Step 1: Project Structure
Let's start with a clean project structure that keeps things organized without overcomplicating things:
portfolio/
├── index.html
├── about.html
├── projects.html
├── contact.html
├── css/
│ └── style.css
├── js/
│ └── main.js
└── assets/
├── images/
└── resume.pdf
Each page is a standalone HTML file. Shared styles live in style.css, and interactivity goes in main.js. Simple, predictable, and easy to maintain.
Step 2: Building the Layout
We'll use a mobile-first approach with CSS custom properties for theming. Here's the foundation:
:root {
--primary: #2563eb;
--dark: #0f172a;
--light: #f8fafc;
--gray: #64748b;
--border: #e2e8f0;
--shadow: 0 4px 14px rgba(0,0,0,0.08);
--radius: 12px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', system-ui, sans-serif;
color: var(--dark);
background: var(--light);
line-height: 1.7;
}
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 1.5rem;
}
Tip: Use system-ui as the font stack. It adapts to the user's operating system font, giving a native feel on every device without loading external fonts.
Step 3: Creating a Reusable Header and Footer
Instead of repeating the same navigation code on every page, we'll use a small JavaScript snippet that loads the header and footer dynamically:
<!-- In each HTML page -->
<div id="header"></div>
<div id="footer"></div>
<script src="js/main.js" defer></script>
// main.js
fetch('components/header.html')
.then(r => r.text())
.then(html => document.getElementById('header').innerHTML = html);
fetch('components/footer.html')
.then(r => r.text())
.then(html => document.getElementById('footer').innerHTML = html);
This keeps your navigation and footer in one place. Update it once, and every page reflects the change.
Step 4: Designing the Hero Section
The hero is the first thing visitors see. Keep it clean and focused:
<section class="hero">
<h1>Hi, I'm Alex</h1>
<p>Full-stack developer & designer</p>
<p>I build fast, accessible websites and web applications.</p>
<a href="/projects" class="btn">View My Work</a>
</section>
.hero {
padding: 6rem 1.5rem;
text-align: center;
background: linear-gradient(135deg, #0f172a, #2563eb);
color: white;
border-radius: 0 0 var(--radius) var(--radius);
}
Step 5: Adding a Projects Grid
Showcase your work with a responsive CSS grid. No frameworks needed:
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.project-card {
background: white;
border-radius: var(--radius);
padding: 1.5rem;
box-shadow: var(--shadow);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.project-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 30px rgba(0,0,0,0.12);
}
The auto-fit and minmax() combination ensures the grid automatically adjusts from 3 columns on desktop to 1 column on mobile — no media queries required.
Step 6: Deploying to Cloudflare Pages
Cloudflare Pages makes deployment incredibly simple. Here's how:
- Push your code to GitHub — Create a repository and push your project.
- Log in to Cloudflare Pages — Go to the Cloudflare dashboard and click Pages.
- Connect your Git provider — Authorize Cloudflare to access your repository.
- Configure build settings — Set the build command to empty (no build step) and publish directory to
/(or your project root). - Deploy — Click "Deploy" and your site will be live in under a minute.
# Cloudflare Pages configuration
Build command: (none)
Publish directory: /
Root directory: /
Pro tip: Add a _redirects file in your publish root to enable clean URLs. For example: /about /about.html 200 turns /about.html into /about.
Step 7: Adding a Custom Domain
Cloudflare Pages lets you add a custom domain with one click:
- Go to your project in the Cloudflare dashboard.
- Click "Custom domains" and enter your domain (e.g.,
alex.dev). - Cloudflare automatically provisions an SSL certificate and sets up DNS.
- HTTPS is enabled automatically and redirects from HTTP to HTTPS are handled for you.
Step 8: Performance Optimization
A portfolio website should load in under 2 seconds. Here's how to keep it fast:
- Optimize images — Use WebP format and compress to under 100KB per image.
- Minimize CSS and JS — Remove unused styles and whitespace. Cloudflare can auto-minify for you.
- Lazy load images — Add
loading="lazy"to images below the fold. - Keep it simple — Every external script or font is a network request. Question each dependency.
<!-- Lazy loading example -->
<img src="project-thumbnail.webp" alt="Project screenshot" loading="lazy">
Conclusion
Building a portfolio website doesn't require a complex framework or a build pipeline. With vanilla HTML, CSS, and JavaScript, you can create a fast, beautiful, and maintainable site that loads instantly and ranks well on search engines.
Cloudflare Pages handles the hosting, CDN, HTTPS, and deployment pipeline for free — letting you focus on what matters most: showcasing your work and attracting clients.
Ready to build yours? Start with a single index.html file, add your content, push to Git, and deploy. Your portfolio could be live in under an hour.